[英]How to type-constrain the entries of a Raku function's array argument?
我试图在Raku
中定义一个子例程,其参数是一个Int数组(将其作为一个约束,即拒绝不是Int
Array
的 arguments)。
问题:实现这一目标的“最佳”(最惯用的,或直截了当的,或您认为“最佳”应指的任何东西)的方法是什么?
下面是在Raku
REPL 中运行的示例。
我希望的工作
> sub f(Int @a) {1}
&f
> f([1,2,3])
Type check failed in binding to parameter '@a'; expected Positional[Int] but got Array ([1, 2, 3])
in sub f at <unknown file> line 1
in block <unit> at <unknown file> line 1
另一个不工作的例子
> sub f(@a where *.all ~~ Int) {1}
&f
> f([1,2,3])
Constraint type check failed in binding to parameter '@a'; expected anonymous constraint to be met but got Array ([1, 2, 3])
in sub f at <unknown file> line 1
in block <unit> at <unknown file> line 1
虽然
> [1,2,3].all ~~ Int
True
有什么作用
两种变体
> sub f(@a where { @a.all ~~ Int }) {1}
和
> sub f(@a where { $_.all ~~ Int }) {1}
给我我想要的:
> f([5])
1
> f(['x'])
Constraint type check failed in binding to parameter '@a'; expected anonymous constraint to be met but got Array (["x"])
in sub f at <unknown file> line 1
in block <unit> at <unknown file> line 1
我过去使用过它,但它让我觉得有点笨拙/冗长..
补充说明
我最初尝试的语法Int @a
并不完全是伪造的,但我不知道它什么时候应该通过 go 什么时候不应该。
例如,我可以在class中执行此操作:
> class A { has Int @.a }
(A)
> A.new(a => [1,2,3])
A.new(a => Array[Int].new(1, 2, 3))
> A.new(a => [1,2,'x'])
Type check failed in assignment to @!a; expected Int but got Str ("x")
in block <unit> at <unknown file> line 1
编辑 1
根据文档,这有效
> sub f(Int @a) {1}
&f
> my Int @a = 1,2,3 # or (1,2,3), or [1,2,3]
> f(@a)
1
但是,如果我在@a
的声明中省略Int
,我会回到之前报告的错误。 如前所述,我无法使用f([1,2,3])
在匿名 arrays 上运行f
。
我认为主要的误解是my Int @a = 1,2,3
和[1,2,3]
在某种程度上是等价的。 他们不是。 第一种情况定义了一个仅采用Int
值的数组。 第二种情况定义了一个可以接受任何内容的数组,并且恰好在其中包含Int
值。
我将尝试涵盖您尝试过的所有版本,为什么它们不起作用,以及它可能是如何工作的。 我将使用一个裸露的dd
作为 function 主体的证明。
#1
sub f(Int @a) { dd }
f([1,2,3])
这不起作用,因为签名接受一个对其容器描述符具有Int
约束的Positional
。 签名绑定只查看 arguments 的约束,而不是值。 观察:
my Int @a; say @a.of; # (Int)
say [1,2,3].of; # (Mu)
say Mu ~~ Int; # False
这种方法没有解决方案,因为没有[ ]
语法可以生成具有Int
约束的Array
。
#2
sub f(@a where *.all ~~ Int) { dd }
这非常接近,但是使用*
并没有做正确的事情。 我不确定这是否是一个错误。
您发现这些解决方案也有效:
sub f(@a where { @a.all ~~ Int }) { dd }
sub f(@a where { $_.all ~~ Int }) { dd }
幸运的是,您不必实际指定显式块。 这也有效:
sub f(@a where @a.all ~~ Int) { dd }
sub f(@a where $_.all ~~ Int) { dd }
除了您找到的@a.all
和$_.all
解决方案之外,还有第三种解决方案:只需扔掉*
!
sub f(@a where .all ~~ Int) { dd }
#3
class A { has Int @.a }
A.new(a => [1,2,3])
这与签名绑定不同。 实际上,在.new
中,您正在执行以下操作:
@!a = [1,2,3]
这很有效,因为您指定的数组中只有Int
值。 正如你所展示的,那里还有其他东西,它会失败。 但这与:
my Int @a = [1,2,"foo"]
失败。
这是对 Liz 已经接受的正确答案的补充。
首先,请注意sub f(Int @a) {...}
和sub f(@a where.all ~~ Int) {...}
之间还有一个额外的区别:第一个检查数组的类型(一个O(1) 操作),而第二个遍历 Array 并检查每个元素的类型(O(n) 操作)。 这出现在上一个问题中,您可能会觉得有帮助。
其次,还有另一种编写f
的方法,它利用了新的强制协议(并且可能是我个人的编写方式):
sub f(Array[Int]() $a) {...}
这将 $a 限制为可以转换为Array[Int]
的任何类型,然后将其绑定到该类型化的 Array。 这与@a where.all ~~ Int
大体相似,只是它使用$
并在 function 中维护类型约束。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.