如何解决如何对 Raku 函数的数组参数的条目进行类型约束?
我试图在 Raku
中定义一个子例程,它的参数是 Array 的 Int(将其作为约束,即拒绝 不是 Array
个 Int
个)。
问题:实现这一目标的“最佳”方式是什么?
示例在 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,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,3])
Constraint type check Failed in binding to parameter '@a'; expected anonymous constraint to be met but got Array ([1,3])
in sub f at <unkNown file> line 1
in block <unit> at <unkNown file> line 1
尽管
> [1,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
并不完全是假的,但我不知道什么时候应该通过,什么时候不通过。
例如,我可以在 class 中执行此操作:
> class A { has Int @.a }
(A)
> A.new(a => [1,3])
A.new(a => Array[Int].new(1,3))
> A.new(a => [1,'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,3 # or (1,3),or [1,3]
> f(@a)
1
但是如果我在 Int
的声明中省略 @a
,我会回到之前报告的错误。如前所述,我无法使用 f
在匿名数组上运行 f([1,3])
。
解决方法
我认为主要的误解是 my Int @a = 1,2,3
和 [1,3]
在某种程度上是等价的。他们不是。第一种情况定义了一个仅采用 Int
值的数组。第二种情况定义了一个可以接受任何东西的数组,并且恰好在其中包含 Int
值。
我将尝试涵盖您尝试过的所有版本、为什么它们不起作用,以及它可能如何起作用。我将使用一个裸 dd
作为到达函数主体的证据。
#1
sub f(Int @a) { dd }
f([1,3])
这不起作用,因为签名接受一个 Positional
,它在其容器描述符上有一个 Int
约束。签名绑定只查看参数的约束,不查看值。观察:
my Int @a; say @a.of; # (Int)
say [1,3].of; # (Mu)
say Mu ~~ Int; # False
这种方法没有解决方案,因为没有 [ ]
语法可以生成带有 Array
约束的 Int
。
#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,3])
这与签名绑定不同。有效地在 .new
中:
@!a = [1,3]
这是可行的,因为您指定的数组中只有 Int
值。正如你所展示的,如果里面还有其他东西,它就会失败。但这与以下没有什么不同:
my Int @a = [1,"foo"]
失败。
,这是对 Liz 已经接受的正确答案的补充。
首先,请注意 os.system
和 sub f(Int @a) {...}
之间还有一个区别:第一个检查数组的类型(O(1) 操作),而第二个遍历数组并检查数组的类型每个元素的类型(O(n) 操作)。这是在 previous question 中提出的,您可能会觉得有用。
其次,还有另一种编写 sub f(@a where .all ~~ Int) {...}
的方法,它利用了新的强制协议(这可能是我个人的编写方式):
f
这将 $a 限制为可以转换为 sub f(Array[Int]() $a) {...}
的任何类型,然后将其绑定到该类型的数组。这与 Array[Int]
大致相似,不同之处在于它使用 @a where .all ~~ Int
并在函数内部维护类型约束。
另一种可能...
sub f(Array[Int]() $array) { $array }
say WHAT f [1,3]; # (Array[Int])
或者:
sub f2(Array[Int]() $array;; @array = $array) { @array }
say WHAT f2 [1,3]; # (Array[Int])
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。