Swift中的咖喱功能

时间:2014-06-08 14:36:02

标签: swift currying

我想创建一个返回咖喱函数的函数,如下面的

func addTwoNumbers(a: Int)(b: Int) -> Int {
    return a + b
}

addTwoNumbers(4)(b: 6) // Result: 10

var add4 = addTwoNumbers(4)
add4(b: 10) // returns 14     

这种函数的返回类型是什么?如何使用采用Variadic参数的函数生成这样的函数。

func generateCurry(.../*Variadic parameters*/) -> .../*curry function type*/ {
  return ...//curry function
}

我想要一个通用解决方案,而不是只将Int作为参数在generateCurry函数的参数中

let curried = curry(func(a, b, c) {
  print(a + b + c)
})
curried(1)(2)(3) //prints 6

4 个答案:

答案 0 :(得分:13)

使用闭包可以轻松实现这一目标:

/// Takes a binary function and returns a curried version
func curry<A,B,C>(f: (A, B) -> C) -> A -> B -> C {
    return { a in { b in f(a, b) } }
}

curry(+)(5)(6) // => 11

let add: Int -> Int -> Int = curry(+)
add(5)(6) // => 11

能够为需要3,4个或更多参数的函数执行相同的操作,但不重复实现,这将是非常好的。这种功能的签名可能会开始:

/// Take a function accepting N arguments and return a curried version
func curry<T>(args: T...) -> /* ? */

返回类型是什么?它会根据函数的输入而改变。这在目前Swift中是不可能的,如果没有某种宏系统,我认为它根本不可能。但即使使用宏,我也不认为编译器会满意,除非它在编译时知道列表的长度。

话虽如此,使用接受3,4,5或更多参数的版本手动重载currying函数真的很直接:

func curry<A,B,C,D>(f: (A, B, C) -> D) -> A -> B -> C -> D {
    return { a in { b in { c in f(a,b,c) } } }
}

func curry<A,B,C,D,E>(f: (A, B, C, D) -> E) -> A -> B -> C -> D -> E {
    return { a in { b in { c in { d in f(a,b,c,d) } } } }
}

// etc.

答案 1 :(得分:1)

我不确定这实际上是否可能像Python这样的语言一样。

我认为拥有单一通用解决方案的核心问题是您想要接受的闭包/函数的强类型。

您可以相当轻松地创建一个咖喱功能,该功能可以处理特定或常见的功能签名,但就通用咖喱而言,我没有看到它的工作方式。问题不仅仅是参数的类型(如评论中所述),还包括它们的数量。

我已经写了一个如何实现咖喱功能的简单示例。它有效,但我没有看到一种理智的方式,就像你可以用更松散的类型语言那样拥有一个真正的通用方法。

func add(a1: Int, a2: Int) -> Int {
    return a1 + a2
}

func curry(argument: Int, block: (Int, Int) -> Int) -> Int -> Int{
    func curried(arg: Int) -> Int {
        return block(argument, arg)
    }

    return curried
}

curry(5, add)(6)

答案 2 :(得分:0)

如果您想快速获取任意数量参数的curry函数,可以按this gist所示生成它。

代码在Swift 2.2中,并为Swift 2.2生成代码(目前)。它使用简单的基于模板的方法(可能的替代方案是构建AST,然后是代码生成):

func genCurry(n: Int, indent: Indent = .fourSpaces, accessLevel: AccessLevel = .Default, verbose: Bool = false) -> String {

    // ...
    // The bulky park is skipped for clarity.

    return accessLevel.asPrefix + "func curry<\(genericParams)>(f: \(fSig)) -> \(curriedSig(n)) {\n"
        + indent.single + "return \(closure)\n"
        + "}\n"
}

答案 3 :(得分:0)

我最近发现currying在Swift3中被删除了。我创建了自己的版本,该版本是重复的,但可以完成工作。

precedencegroup CurryPrecedence {
    associativity: left
    higherThan: MultiplicationPrecedence
}
infix operator <<== :CurryPrecedence
//1 param
func <<==<A,Z>(_ f: @escaping (A) -> (Z), _ p:A) -> () -> (Z)  {
    { f(p) }
}
//2 param
func <<==<A,B,Z>(_ f: @escaping (A, B) -> (Z), _ p:B) -> (A) -> (Z)  {
    { (A) in f(A,p) }
}
//3 param
func <<==<A,B,C,Z>(_ f: @escaping (A, B, C) -> (Z), _ p:C) -> (A, B) -> (Z)  {
    { (A, B) in f(A,B,p) }
}
//4 param
func <<==<A,B,C,D,Z>(_ f: @escaping (A, B, C, D) -> (Z), _ p:D) -> (A, B, C) -> (Z)  {
    { (A, B, C) in f(A,B,C,p) }
}

要使用它:

let ten = (addTwoNumbers <<== 6 <<== 4)()

let ten = (addTwoNumbers <<== 6)(4)