【问题标题】:Curry Function in SwiftSwift 中的咖喱函数
【发布时间】:2020-11-19 19:35:52
【问题描述】:

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

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     

这种函数的返回类型是什么?如何使用带有可变参数的函数生成这样的函数。

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

【问题讨论】:

  • 嗨,那么您想要做的是:一个将另一个函数作为参数的函数,并且在该函数内能够根据类型推断要调用哪个函数?此外,您的 curried 现在打印 6,但是当它是 String 类型时,您想做什么?
  • 你可以有任何类型,但关键是只有在所有参数都被传递之后才评估在 curry 函数中传递的代码块。我已更新问题以显示使用 addTwoNumbers 方法的示例
  • 我发现这篇文章russbishop.net/swift-function-currying 非常有用且解释清楚

标签: swift currying


【解决方案1】:

你可以用闭包很容易地做到这一点:

/// 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 或更多参数的版本手动重载柯里化函数真的很简单:

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.

【讨论】:

  • 很棒的答案!谢谢!
【解决方案2】:

我不确定这实际上是否会像 Python 等语言一样实现。

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

您可以相当轻松地创建一个适用于特定或通用函数签名的 curry 函数,但就通用 curry 而言,我看不到它可以工作的方法。问题不仅仅在于参数的类型(如 cmets 中所述),还在于它们的数量。

我已经写了一个简单的例子来说明如何实现一个 curry 函数。它有效,但我认为没有一种理智的方法可以像在更松散类型的语言中那样拥有真正通用的方法。

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)

【讨论】:

  • 我们可以使用泛型并确保所有传递的参数都是 T 类型
  • 您仍然会遇到这样一个事实,即具有不同数量参数的函数是不同的签名,因此是不同的类型。可变参数要求是问题的一部分。
  • 如果我们可以解决在可变参数函数中传递数组的问题,那么我们就可以解决这个问题。语言中有没有一种方法可以在 Objective C 中调用像 performSelector 这样的可变参数方法,或者我们可以桥接到 Objective C 以使其工作?
  • 在 swift 中,可变参数的实现似乎实际上是一个数组。我只是尝试实现我的示例的一个版本,它使用 Any 来达到某种效果。我能够使用它使 Xcode 崩溃,并通过执行 var test: Any = curry(5,add) 得到一个真正令人印象深刻的编译器错误。我一直在回到一个普通的咖喱是不可能的,或者比它值得付出的努力最少。
【解决方案3】:

如果您想快速获取任意数量参数的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"
}

【讨论】:

    【解决方案4】:

    我最近发现 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)
    

    【讨论】:

      猜你喜欢
      • 2016-04-10
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      相关资源
      最近更新 更多