我想知道如何实现一个无限的咖喱加法函数,在解释的情况下我会坚持使用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)
答案 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