我想写一个函数,返回一个数组,其所有子数组的长度必须为2。例如,return将为[[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]];
}
我发现(1)过于复杂。有没有更简单的东西?
发布于 2021-09-04 08:21:36
先退一步
subset TArray of Array where { .all ~~ subset :: where [Int, Int] };
有没有更简单的东西?
在我们开始之前,让我们退后一步。即使仅仅看一下代码就忽略了它的“过于复杂”的性质,由于各种可能不那么明显的原因,它也有潜在的问题和复杂性。我将重点介绍三点:
subset
将接受包含Array
s的Array
,其中每个数组包含两个Int
s。但它不强制使用Array[Array[Int]]
。外部Array
的类型可能只是一个泛型Array
,而不是一个让Array[Array[Int]]
单独存在的Array[Array]
。事实上,除非您有意引入强类型的值,否则它将是。我将在本答案的最后一节介绍强类型。Array
呢?你的subset
会接受的。这就是你的意图吗?如果不是,那么至少需要一对Int
s?怎么办?
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);
这都避免了连接/smartmatch问题。(第一个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)
这些值具有由其文字constructors.确定的类型
Array
,它们的元素是泛型的。(第二个不是Array[Int]
,这可能是意料之中的。类似地,最后一个不是Array[Array[Int]]
。)
当前内置的复合类型(数组和散列)的Raku文字形式都构造泛型Array
,它们不限制其元素的类型。
请参阅PR Introduce [1,2,3]:Int
syntax #4406,了解有关元素类型复合文字的提案/ PR,以及有关该PR的替代和/或补充方法的a related issue I just posted in response to your Q here。(多年来一直在讨论类型系统的这一方面,但现在似乎是Rakoons考虑解决这个问题的时候了。)
如果您想构建一个强类型的数据结构作为从例程中返回的值,并让返回类型对此进行检查,该怎么办?
下面是构建这样一个强类型值的一种方法:
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);
}
构建强类型值的另一种方法是不仅在变量的类型约束中指定强类型,而且还指定从松散类型值到强类型目标的。
我们保持完全相同的subset
(它建立了强类型的目标数据结构并添加了“细化类型”检查):
subset TArray of Array[Array[Int]] where 0 < .elems == .grep: (Int,Int);
但是,我们没有使用冗长的按构造更正的初始化值,而是使用完整的类型名和new
,我们引入了额外的强制类型,然后只使用普通的文字语法:
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
可以轻松地完成这项工作。)
发布于 2021-09-05 15:23:23
我猜测其目的是将内部数组的类型限制为Int,Int…我能做到的最接近的方法是声明两个子集,一个基于另一个子集。
subset IArray where * ~~ [Int, Int];
subset TArray where .all ~~ IArray;
否则,你使用的匿名子集形式似乎是最简短的,尽管@raiph指出你可以去掉'of Array‘这一部分。
发布于 2021-09-07 15:57:22
如果你想对一个函数的参数(而不是它的返回类型)施加这样的约束,你可以这样做:
sub fcn(@a where {all .map: * ~~ [Int, Int]}) {...}
正如其他答案所提到的,目前还没有很好的语法来类似地约束返回类型,但有一个建议是使用add support for similar syntax for return types。事实上,正如在那个问题中提到的,有人自愿致力于实现,但据我所知还没有取得任何进展。(我想我应该知道,因为我是那个志愿者…(哦)
因此,就目前而言,子集是最好的选择-但希望将来会有更好的方法来编写它。
https://stackoverflow.com/questions/69053265
复制相似问题