繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM