无限的咖喱添加功能

时间:2019-07-17 20:26:39

标签: scala currying

我想知道如何实现一个无限的咖喱加法函数,在解释的情况下我会坚持使用scala。

我知道如何准备像

这样的简单咖喱
def add(a: Int): Int => Int = {
  def iadd(b: Int): Int = {
    a + b
  }
  iadd
}
add(4)(5) // 9

我将如何实施add(5)(4)(x1)(x2)..(xn)

3 个答案:

答案 0 :(得分:4)

聪明的方式

问题是评论的位置很恰当:什么时候停止计算并产生结果?

一种解决方案是通过使用零参数调用函数来停止递归。 Scala的超载让我们做到这一点。

add(1)(2)(3)(4)() // The () indicates that we're done currying

这是相对简单的。我们只需要一个带有apply的类即可返回自身的新实例

// A class with an apply method is callable like a function
class Adder(val acc: Int) {

  def apply(a: Int): Adder =
    new Adder(acc + a)

  def apply(): Int =
    acc

}

def add: Adder = new Adder(0)

println(add(1)(2)(3)(4)()) // 10

如果您确实有这样做的理由,那将是我推荐的方式。它很简单,易于阅读,并且在currying之上添加了很少的样板。

微不足道的方式

但是简单而合乎逻辑的是什么呢?让我们摆脱最后的那些傻括号​​,是吗?我们可以使用Scala的隐式转换来做到这一点。首先,我们需要导入功能,以便Scala停止警告我们正在做的事情很愚蠢而不是个好主意。

import scala.language.implicitConversions

然后我们做到这一点,以便可以将Adder转换为Int

// Don't do this in real code
implicit def adderToInt(adder: Adder): Int =
  adder()

现在,我们不需要在最后加上这些括号。但是,我们确实需要向类型系统指示我们想要Int

val result: Int = add(1)(2)(3)(4)
println(result) // 10

例如,将结果传递给采用Int的函数就足够了。

评论

由于您通常提到函数式编程,因此我将注意到您可以使用类型类在Haskell中完成类似的技巧。您可以使用Text.PrintF在标准库中看到此操作。请注意,由于Haskell函数始终使用一个参数,因此您需要有一个哨兵值来指示参数的“结尾”(()就足够了,这取决于参数类型的通用性)。

答案 1 :(得分:3)

如果您想将每个整数n重新解释为函数n.+,则只需执行以下操作:

implicit class Add(val x: Int) extends AnyVal { def apply(i: Int) = x + i }
val add = 0

或更短(含隐式转换):

implicit def asAdd(n: Int): Int => Int = n.+
val add = 0

示例:

add(1)(2)(3)(4) // res1: Int = 10

没有“无限咖喱”之类的东西,这不是有意义的概念。

答案 2 :(得分:2)

好吧,这并非完全无限,但它为您提供了类似的东西。

final class InfiniteCurrying[A, B] private (acc: A, op: (A, B) => A) {
  final val run: A = acc

  final def apply(b: B): InfiniteCurrying[A, B] =
    new InfiniteCurrying(
      acc = op(acc, b),
      op,
    )
}

object InfiniteCurrying {
  def add(initial: Int): InfiniteCurrying[Int, Int] =
    new InfiniteCurrying(
      acc = initial,
      op = (acc, b) => acc + b
    )
}

import InfiniteCurrying._

val r = add(10)(20)(30)
r.run // res: Int = 60