先退一步
subset TArray of Array where { .all ~~ subset :: where [Int, Int] };
还有更简单的吗?
在我们去那里之前,让我们退后一步。即使仅基于查看代码而忽略代码“过于复杂”的性质,它也可能会因各种原因而存在问题和复杂性,而这些原因可能并不那么明显。我将强调三个:
-
这个subset 将接受一个包含Arrays 的Array,其中每个数组都包含两个Ints。但它不强制要求Array[Array[Int]]。外部Array 的类型 可能只是一个通用的Array,而不是一个Array[Array] 让我们单独使用一个Array[Array[Int]]。事实上它会除非你故意引入强类型值。我将在此答案的最后一部分介绍强类型。
-
空的Array 怎么样?你的subset 会接受。这是你的意图吗?如果不是,那么要求至少一对 Ints 怎么样?
-
外部where 子句使用.all ~~ ... 形式的常见Raku 成语,junction 在~~ smart match operator 的左侧。令人惊讶的是,根据an issue I just filed,这可能是个问题。有什么替代品?
从简单开始
Raku 在简单的事情上做得不错。如果我们抛开对强类型的人为需求,专注于简化代码的简单工具,我过去建议的一个简单子集是:
subset TArray where .all == 2; # BAD despite being idiomatic???
这包含原始代码的所有问题,此外它还接受具有整数所属的非整数的数据。
但它确实具有可以进行有用检查的可取之处(内部数组每个都有两个元素),并且比您的代码简单得多。
现在我已经提醒自己,我需要将~~ 左侧的.all 视为可能有问题,我将改为:
subset TArray where 2 == .all; # Potentially the new idiomatic.
这个版本读起来更差,但是,虽然可读性很重要,但基本的正确性更重要。
还是比较简单,问题少
这是我想出的两个变体:
subset TArray where all .map: * ~~ (Int,Int);
subset TArray where .elems == .grep: (Int,Int);
这些都避免了连接/智能匹配问题。 (第一个 where 表达式确实在智能匹配的左侧有一个连接点,但这不是问题的一个例子。)
第二个版本显然不是很正确(可以将其视为检查子数组的计数是否与匹配 (Int,Int) 的子数组的计数相同),但如果存在匹配问题,它很好地解决了匹配问题是零子数组,如果需要修复:
subset TArray where 0 < .elems == .grep: (Int,Int);
强类型解决方案
迄今为止的解决方案不处理强类型。也许这是可取的。也许不是。
要理解我的意思,让我们先看一下字面量:
say WHAT 1; # (Int)
say WHAT [1,2]; # (Array)
say WHAT [[1,2],[3,4]]; # (Array)
如果您想构建一个强类型数据结构作为从您的例程返回的值,并让返回类型检查该值怎么办?
这是构建强类型值的一种方法:
my Array[Array[Int]] $result .= new: Array[Int].new(1,2), Array[Int].new(3,4);
超级详细!但是现在你可以为你的 sub 的返回类型检查编写以下内容,它会起作用:
subset TArray of Array[Array[Int]] where 0 < .elems == .grep: (Int,Int);
sub fcn(Int $n) of TArray is export(:fcn) {
my Array[Array[Int]] $result .= new: Array[Int].new(1,2), Array[Int].new(3,4);
}
另一种构建强类型值的方法是,不仅在变量的类型约束中指定强类型,而且还指定 coercion typing 以从松散类型值桥接到强类型目标。
我们保持完全相同的subset(建立强类型目标数据结构并添加“细化类型”检查):
subset TArray of Array[Array[Int]] where 0 < .elems == .grep: (Int,Int);
但我们没有使用冗长的正确构造初始化值,而是使用完整的类型名称和news,而是引入了额外的强制类型,然后只使用普通的文字语法:
constant TArrayInitialization = TArray(Array[Array[Int]()]());
sub fcn(Int $n) of TArray is export(:fcn) {
my TArrayInitialization $result = [[1,2],[3,4]];
}
(我本可以将 TArrayInitialization 声明写成另一个 subset,但这样做有点矫枉过正。constant 可以轻松完成这项工作。)