Ceylon中reduce()的签名

Iterable接口使用以下签名定义了一个名为fold()的方法:

Result fold<Result>(Result initial,
        Result accumulating(Result partial, Element elem))

其中ElementIterable的元素类型。 此方法接受一个初始值和一个累加器函数,该函数又依次应用于可迭代对象的每个元素。 例如:

Integer sum = (1..10).fold(0, plus<Integer>);

有时,我们不需要初始值,因为可以从第一个元素开始累加。 遵循Scala和F#使用的约定,让我们将此函数称为reduce() 。 然后,我们希望能够编写:

Integer sum = (1..10).reduce(plus<Integer>);

但是这种方法的签名应该是什么? 第一个刺可能会给我们:

Element reduce(Element accumulating(Element partial, Element elem))

但是此签名比应有的限制更多。 将reduce()的结果类型作为元素类型的超类型是完全合理的。 Scala使用下限类型约束来处理此问题。 使用虚构的下限语法将其译为锡兰,它看起来像:

Result reduce<Result>(Result accumulating(Result partial, Element elem))
        given Result abstracts Element

在此,下限约束确保将第一个元素分配给累加器函数的第一个参数。 但是Ceylon没有下界类型约束。 为什么? 好吧,因为似乎实际上我们几乎可以使用联合类型来实现相同的效果。 因此,让我们尝试:

Result|Element reduce<Result>(
        Result accumulating(Result|Element partial, Element elem))

现在,让我们尝试实现此签名。 一种可能性是:

Result|Element reduce<Result>(
        Result accumulating(Result|Element partial, Element elem)) {
    assert (!empty, is Element initial = first);
    variable Result|Element partial = initial;
    for (elem in rest) {
        partial = accumulating(partial, elem);
    }
    return partial;
}

断言将处理Iterable为空的情况,如果iterable对象没有第一个元素,则会导致AssertionException

另外,在Iterable为空的情况下,我们可能更希望返回null ,这建议以下实现:

Result|Element|Null reduce<Result>(
        Result accumulating(Result|Element partial, Element elem)) {
    if (!empty, is Element initial = first) {
        variable Result|Element partial = initial;
        for (elem in rest) {
            partial = accumulating(partial, elem);
        }
        return partial;
    }
    else {
        return null;
    }
}

回到Scala,我们注意到Scala具有reduce()两个版本,它们与我们刚刚看到的两种可能性完全相似。 第一个版本在空情况下引发异常,第二个版本reduceOption()返回包装器类Option的实例。

但是在锡兰,我们可以做得更好。 在锡兰, Iterable具有一个看上去有点神秘的第二类型参数,名为Absentgiven Absent satisfies Null的上限将given Absent satisfies Null 。 我们通常写为{T*}Iterable<T,Null>是一个可能为空的可迭代对象。 我们通常写为{T+}Iterable<T,Nothing>是一个我们知道为非空的可迭代对象。

因此,我们得出了reduce()的以下定义:

Result|Element|Absent reduce<Result>(
        Result accumulating(Result|Element partial, Element elem)) {
    value initial = first;
    if (!empty, is Element initial) {
        variable Result|Element partial = initial;
        for (elem in rest) {
            partial = accumulating(partial, elem);
        }
        return partial;
    }
    else {
        return initial;
    }
}

现在,对于“跨越”范围表达式像1..n ,这是非空的,我们得到一个非空的返回类型:

Integer sum = (1..n).reduce(plus<Integer>);

另一方面,对于像1:n这样的“分段”范围表达式,可能是空的,我们得到一个可选的返回类型:

Integer? sum = (1:n).reduce(plus<Integer>);

最棒的是,它永远不会引发异常。 这是我谦卑地提交的“该死的尼斯”。

在这里注意工会类型为我们做了多少工作。 与Scala的reduce() / reduceOption() ,它们让我们消除了:

  • 下界类型约束,
  • 第二个有效重载的方法版本,以及
  • 包装器Option类。

我已经将reduce()定义添加到Iterable ,它将在下一版Ceylon中可用。

参考: Ceylon团队博客博客上的JCG合作伙伴 Gavin King在Ceylon中 的reduce()签名

翻译自: https://www.javacodegeeks.com/2014/01/the-signature-of-reduce-in-ceylon.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值