我想编写一个函数,返回一个数组,其所有子数组的长度必须为 2。例如返回将是[[1, 2], [3, 4]].
我定义:
(1) subset TArray of Array where { .all ~~ subset :: where [Int, Int] };
和
sub fcn(Int $n) of TArray is export(:fcn) {
[[1, 2], [3, 4]];
}
Run Code Online (Sandbox Code Playgroud)
我发现(1)过于复杂。有没有更简单的?
subset TArray of Array where { .all ~~ subset :: where [Int, Int] };
Run Code Online (Sandbox Code Playgroud)
有没有更简单的东西?
在我们去那里之前,让我们退后一步。即使仅仅根据代码的外观而忽略代码的“过于复杂”的性质,它也可能由于各种可能不那么明显的原因而出现问题和复杂。我重点强调三点:
这subset将接受一个Array包含Arrays,其中每个数组都包含两个Ints。但它并不强制要求Array[Array[Int]]. 外部Array的类型可能只是一个泛型Array,而不是一个Array[Array]单独的类型Array[Array[Int]]。事实上,除非您故意引入强类型值,否则确实如此。我将在本答案的最后一部分介绍强类型。
空的话怎么办Array?你subset会接受这一点。这是你的意图吗?如果没有,至少需要一对怎么样Int?
外部where子句使用常见的 Raku 习语形式.all ~~ ...,在智能匹配运算符的左侧有一个连接点。令人惊讶的是,根据我刚刚提交的问题,这可能是一个问题。还有哪些替代方案?~~
Raku 在让简单的事情变得简单方面做得很好。如果我们抛开对强类型的任何人为渴望,并专注于收紧代码的简单工具,我过去建议的一个简单子集将是:
subset TArray where .all == 2; # BAD despite being idiomatic???
Run Code Online (Sandbox Code Playgroud)
这具有原始代码所存在的所有问题,此外它还接受包含整数所属的非整数的数据。
但它确实具有可取之处,它进行了有用的检查(内部数组每个都有两个元素),并且它比您的代码简单得多。
现在我提醒自己,我需要查看可能存在问题.all的左侧,我会将其写为:~~
subset TArray where 2 == .all; # Potentially the new idiomatic.
Run Code Online (Sandbox Code Playgroud)
这个版本的可读性较差,但是,虽然可读性很重要,但基本正确性更重要。
这是我想出的两种变体:
subset TArray where all .map: * ~~ (Int,Int);
subset TArray where .elems == .grep: (Int,Int);
Run Code Online (Sandbox Code Playgroud)
这些都避免了连接/智能匹配问题。(第一个where表达式确实在智能匹配左侧有一个连接点,但这不是问题的示例。)
第二个版本不是那么明显正确(可以将其视为检查子数组的计数是否与匹配的子数组的计数相同(Int,Int)),但它很好地解决了子数组为零的情况下的匹配问题,如果是的话需要修复:
subset TArray where 0 < .elems == .grep: (Int,Int);
Run Code Online (Sandbox Code Playgroud)
迄今为止的解决方案不处理强类型。也许这是可取的。也许不是。
为了理解我的意思,让我们首先看一下文字:
say WHAT 1; # (Int)
say WHAT [1,2]; # (Array)
say WHAT [[1,2],[3,4]]; # (Array)
Run Code Online (Sandbox Code Playgroud)
这些值的类型由它们的文字构造函数确定。
最后两个只是Arrays,对其元素具有通用性。
(第二个不是,这可能是预期的Array[Int]。同样,最后一个也不是。Array[Array[Int]])
当前内置的复合类型(数组和散列)的 Raku 文字形式都构造了Array不限制其元素类型的泛型。
请参阅 PR介绍[1,2,3]:Int语法 #4406,了解有关元素类型复合文字的提案/PR,以及我刚刚发布的相关问题,以响应您的问题,了解该 PR 的替代和/或补充方法。(多年来一直在讨论类型系统的这个方面,但似乎现在是 Rakoons 考虑解决这个问题的时候了。)
如果您想构建一个强类型数据结构作为从例程返回的值,并让返回类型检查该结构,该怎么办?
这是构建这样一个强类型值的一种方法:
my Array[Array[Int]] $result .= new: Array[Int].new(1,2), Array[Int].new(3,4);
Run Code Online (Sandbox Code Playgroud)
超级啰嗦!但现在您可以为子程序的返回类型检查编写以下内容,它将起作用:
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);
}
Run Code Online (Sandbox Code Playgroud)
构建强类型值的另一种方法是不仅在变量的类型约束中指定强类型,而且还指定强制类型以从松散类型值桥接到强类型目标。
我们保持完全相同subset(建立强类型目标数据结构并添加“细化类型”检查):
subset TArray of Array[Array[Int]] where 0 < .elems == .grep: (Int,Int);
Run Code Online (Sandbox Code Playgroud)
但是,我们没有使用详细的构造正确初始化值,而是使用完整的类型名称和news,而是引入了额外的强制类型,然后只使用普通的文字语法:
constant TArrayInitialization = TArray(Array[Array[Int]()]());
sub fcn(Int $n) of TArray is export(:fcn) {
my TArrayInitialization $result = [[1,2],[3,4]];
}
Run Code Online (Sandbox Code Playgroud)
(我本可以将TArrayInitialization声明写为 another subset,但这样做有点矫枉过正。 Aconstant可以轻松完成这项工作。)