如何对 Raku 函数的数组参数的条目进行类型约束?
How to type-constrain the entries of a Raku function's array argument?
我正在尝试在 Raku
中定义一个子例程,其参数是 Array of Ints(将其作为约束,即拒绝 不是 Array
s 共 Int
s).
问题:实现该目标的“最佳”(最惯用的,或最直接的,或您认为 'best' 应该是什么意思)的方法是什么?
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
我过去用过它,但它给我的印象有点 clumsy/verbose..
补充说明
我最初尝试的语法 Int @a
并不完全是假的,但我不知道它什么时候应该通过,什么时候不应该通过。
例如,我可以在 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
根据 the docs,这有效
> 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
f([1,2,3])
.
我认为主要的误解是 my Int @a = 1,2,3
和 [1,2,3]
在某种程度上是等价的。他们不是。第一种情况定义了一个数组,它将 仅 取 Int
值。第二种情况定义了一个数组,它可以接受任何东西,并且恰好有 Int
个值。
我将尝试涵盖您尝试过的所有版本,它们为什么不起作用,以及它将如何 起作用。我将使用裸 dd
作为已到达函数主体的证明。
#1
sub f(Int @a) { dd }
f([1,2,3])
这不起作用,因为签名接受一个 Positional
,而该 Positional
在其容器描述符上有一个 Int
约束。签名绑定仅查看参数的约束,不查看值。观察:
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) 操作),而第二个遍历数组和检查每个元素的类型(O(n) 操作)。这出现在 中,您可能会发现它有帮助。
其次,还有另一种编写 f
的方法,它利用了新的强制协议(我个人可能会这样写):
sub f(Array[Int]() $a) {...}
这将 $a 限制为可以转换为 Array[Int]
的任何类型,然后将其绑定到该类型化数组。这与 @a where .all ~~ Int
大致相似,只是它使用 $
并在函数内部维护类型约束。
我正在尝试在 Raku
中定义一个子例程,其参数是 Array of Ints(将其作为约束,即拒绝 不是 Array
s 共 Int
s).
问题:实现该目标的“最佳”(最惯用的,或最直接的,或您认为 'best' 应该是什么意思)的方法是什么?
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
我过去用过它,但它给我的印象有点 clumsy/verbose..
补充说明
我最初尝试的语法 Int @a
并不完全是假的,但我不知道它什么时候应该通过,什么时候不应该通过。
例如,我可以在 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
根据 the docs,这有效
> 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
f([1,2,3])
.
我认为主要的误解是 my Int @a = 1,2,3
和 [1,2,3]
在某种程度上是等价的。他们不是。第一种情况定义了一个数组,它将 仅 取 Int
值。第二种情况定义了一个数组,它可以接受任何东西,并且恰好有 Int
个值。
我将尝试涵盖您尝试过的所有版本,它们为什么不起作用,以及它将如何 起作用。我将使用裸 dd
作为已到达函数主体的证明。
#1
sub f(Int @a) { dd }
f([1,2,3])
这不起作用,因为签名接受一个 Positional
,而该 Positional
在其容器描述符上有一个 Int
约束。签名绑定仅查看参数的约束,不查看值。观察:
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) 操作),而第二个遍历数组和检查每个元素的类型(O(n) 操作)。这出现在
其次,还有另一种编写 f
的方法,它利用了新的强制协议(我个人可能会这样写):
sub f(Array[Int]() $a) {...}
这将 $a 限制为可以转换为 Array[Int]
的任何类型,然后将其绑定到该类型化数组。这与 @a where .all ~~ Int
大致相似,只是它使用 $
并在函数内部维护类型约束。