乐:返回类型

use*_*747 5 raku

我想编写一个函数,返回一个数组,其所有子数组的长度必须为 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)过于复杂。有没有更简单的?

rai*_*iph 7

先退后一步

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可以轻松完成这项工作。)