【问题标题】:Raku: return TypeRaku:返回类型
【发布时间】:2021-09-04 07:29:46
【问题描述】:

我想编写一个函数,返回一个数组,其所有子数组的长度必须为 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]];
}

我发现 (1) 过于复杂。有没有更简单的?

【问题讨论】:

    标签: raku


    【解决方案1】:

    先退一步

    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)
    
    • 这些值的类型由其文字构造函数确定。

    • 最后两个只是Arrays,它们的元素是通用的。

      (第二个不是Array[Int],这可能是意料之中的。同样,最后一个不是Array[Array[Int]]。)

      当前为复合类型(数组和散列)内置的 Raku 文字形式都构造了通用的Arrays,它不限制其元素的类型。

      请参阅 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);
    }
    

    另一种构建强类型值的方法是,不仅在变量的类型约束中指定强类型,而且还指定 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 可以轻松完成这项工作。)

    【讨论】:

    • ``` 子集 TArray where .all == 2; TArray 的子 fcn(Int $n) 是 export(:fcn) { [[1.1, "a"], [3, 4]]; } ``` 有效,但不应该。
    • @p6steve:我认为 op 只想要几个 Int's。
    • @user3166747 "subset TArray where .all == 2; sub fcn(Int $n) of TArray is export(:fcn) { [[1.1, "a"], [3, 4]]; } 有效,但不应该。"我想你的意思是“应该”是 a 方式,即使不是那个特定的代码。在您撰写评论后,我编辑了我的答案,以尝试更好地呈现我所理解的当前可能性。 Raku(do) 继续改进,我希望“很快”会有一种简洁的方式来表达,在一个命名的返回类型中,强类型和强制类型的所需组合我在答案的结尾 强类型中显示解决方案部分。
    【解决方案2】:

    我认为目的是将内部 Array 的类型限制为 [Int,Int] ...我能做到的最接近的方法是声明两个子集,一个基于另一个...

    subset IArray where * ~~ [Int, Int];
    subset TArray where .all ~~ IArray;
    

    否则,您使用的匿名子集形式似乎是最简短的,尽管正如@raiph 指出的那样,您可以删除“数组”部分。

    【讨论】:

      【解决方案3】:

      如果你想对函数的参数(而不是它的返回类型)施加这种约束,你可以这样做:

      sub fcn(@a where {all .map: * ~~ [Int, Int]}) {...}
      

      正如其他答案所提到的,目前没有很好的语法来类似地限制返回类型,但有一个对add support for similar syntax for return types 的建议。事实上,正如那个问题中提到的,有人自愿参与实施,但据我所知尚未取得任何进展。 (我想我应该知道,因为我是那个志愿者……哎呀)

      所以,就目前而言,子集是最好的选择——但希望未来会有更好的方法来编写它。

      【讨论】:

        猜你喜欢
        • 2021-03-11
        • 2014-12-09
        • 2023-03-31
        • 2021-04-15
        • 2020-02-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-07
        相关资源
        最近更新 更多