如何对 Raku 函数的数组参数的条目进行类型约束?

gro*_*ber 10 types constraints raku

我试图定义一个子程序Raku,其理由是,比方说,一个阵列诠释S(强加的约束,即拒绝那些参数不是 ArrayS的IntS)。

问题:实现这一目标的“最佳”方式是什么(最惯用的,或最直接的,或者您认为“最佳”在这里应该是什么意思)?

RakuREPL 中运行的示例如下。

我所希望的会奏效

> 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
Run Code Online (Sandbox Code Playgroud)

另一个非工作示例

> 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
Run Code Online (Sandbox Code Playgroud)

虽然

> [1,2,3].all ~~ Int
True
Run Code Online (Sandbox Code Playgroud)

什么工作

两种变体

> sub f(@a where { @a.all ~~ Int }) {1}
Run Code Online (Sandbox Code Playgroud)

> sub f(@a where { $_.all ~~ Int }) {1}
Run Code Online (Sandbox Code Playgroud)

给我我想要的:

> 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
Run Code Online (Sandbox Code Playgroud)

我过去使用过它,但它让我觉得有点笨拙/冗长..

补充说明

Int @a我最初尝试的语法并不完全是假的,但我不知道什么时候应该通过,什么时候不应该通过。

例如,我可以在课堂上做到这一点:

> 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
Run Code Online (Sandbox Code Playgroud)

编辑 1

根据文档,这有效

> sub f(Int @a) {1}
&f

> my Int @a = 1,2,3 # or (1,2,3), or [1,2,3]
> f(@a)
1
Run Code Online (Sandbox Code Playgroud)

但是如果我Int在声明中省略,@a我会回到之前报告的错误。如前所述,我无法f在匿名数组上运行,使用f([1,2,3]).

Eli*_*sen 8

我认为主要的误解是,my Int @a = 1,2,3并且[1,2,3]在某种程度上是等价的。他们不是。第一种情况定义了一个接受Int值的数组。第二种情况定义了一个可以接受任何东西的数组,并且恰好在其中包含Int值。

我会尽量覆盖你试过了,为什么他们没有工作的所有版本,并可能如何工作。我将使用一个裸dd作为到达函数体的证据。

#1

sub f(Int @a) { dd }
f([1,2,3])
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为签名接受对其容器描述符PositionalInt约束的a 。签名绑定仅查看参数的约束而不查看值。观察:

my Int @a; say @a.of;   # (Int)
say [1,2,3].of;         # (Mu)
say Mu ~~ Int;          # False
Run Code Online (Sandbox Code Playgroud)

这种方法没有解决方案,因为没有[ ]生成ArrayInt约束的语法。

#2

sub f(@a where *.all ~~ Int) { dd }
Run Code Online (Sandbox Code Playgroud)

这是非常接近的,但使用*是不正确的。我不确定这是否是一个错误。

您发现这些解决方案也有效:

sub f(@a where { @a.all ~~ Int }) { dd }
sub f(@a where { $_.all ~~ Int }) { dd }
Run Code Online (Sandbox Code Playgroud)

幸运的是,你不能有实际指定明确的块。这也有效:

sub f(@a where @a.all ~~ Int) { dd }
sub f(@a where $_.all ~~ Int) { dd }
Run Code Online (Sandbox Code Playgroud)

除了您找到的@a.all$_.all解决方案之外,还有第三个解决方案:将*!

sub f(@a where .all ~~ Int) { dd }
Run Code Online (Sandbox Code Playgroud)

#3

class A { has Int @.a }
A.new(a => [1,2,3])
Run Code Online (Sandbox Code Playgroud)

这与签名绑定不同。有效地在.new你做的是:

@!a = [1,2,3]
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为Int您指定的数组中只有值。正如你所展示的,如果里面还有其他东西,它就会失败。但这与:

my Int @a = [1,2,"foo"]
Run Code Online (Sandbox Code Playgroud)

失败。


cod*_*ons 5

这是对莉兹已经接受的正确答案的补充。

sub f(Int @a) {...}首先,请注意和之间还有一个额外的区别sub f(@a where .all ~~ Int) {...}:第一个检查数组的类型(O(1) 操作),而第二个迭代数组并检查每个元素的类型(O(n) 操作)。这在上一个问题中出现过,您可能会发现它很有帮助。

其次,还有另一种f利用新强制协议的编写方式(这可能是我个人的编写方式):

sub f(Array[Int]() $a) {...}
Run Code Online (Sandbox Code Playgroud)

这将 $a 限制为任何可以转换为 an 的类型Array[Int],然后将其绑定到该类型化数组。这与 大致相似@a where .all ~~ Int,只是它使用 a$并维护函数内部的类型约束。

  • 谢谢!答案添加了有价值的信息,前面问题的链接也是如此。顺便说一句,让我注意到,前面的问题可能会(取消)我的重复问题,但人们似乎对“SO”的“Raku”角落里的这类事情不太高兴。也谢谢你!:) (4认同)