代码之家  ›  专栏  ›  技术社区  ›  Eric M

在函数式编程中什么是“curring”?[复制品]

  •  43
  • Eric M  · 技术社区  · 16 年前

    我曾在几篇文章和博客中看到过咖喱函数的引用,但是我找不到一个好的解释(或者至少有一个有意义的解释)。

    0 回复  |  直到 6 年前
        1
  •  745
  •   OneCricketeer Gabriele Mariotti    7 年前

    curring是将一个接受多个参数的函数分解为一系列接受部分参数的函数。以下是javascript中的一个示例:

    function add (a, b) {
      return a + b;
    }
    
    add(3, 4); // returns 7
    

    这个函数接受两个参数a和b,并返回它们的和。我们现在将使用curry函数:

    function add (a) {
      return function (b) {
        return a + b;
      }
    }
    

    这个函数接受一个参数a,并返回一个接受另一个参数b的函数,这个函数返回它们的和。

    add(3)(4);
    
    var add3 = add(3);
    
    add3(4);
    

    第一条语句返回7,就像add(3,4)语句一样。第二条语句定义了一个名为add3的新函数,该函数将在其参数中添加3。这就是一些人所说的结束。第三条语句使用add3操作将3添加到4,结果再次生成7。

        2
  •  117
  •   Alex Martelli    16 年前

    在函数代数中,处理带多个参数的函数(或者等价的一个参数是一个n元组)有点不雅观——但是,正如Moses Sch_《Nfinkel》(以及独立的Haskell Curry)所证明的那样,不需要:只需要带一个参数的函数。

    那么你如何处理你自然会表达的东西,比如说, f(x,y) ?好吧,你认为这相当于 f(x)(y) —— f(x) 称之为 g ,是一个函数,您可以将该函数应用于 y . 换句话说,您只有接受一个参数的函数——但是其中一些函数返回其他函数(也接受一个参数;-)。

    像往常一样, wikipedia 有一个很好的关于这个的摘要条目,有很多有用的指针(可能包括你最喜欢的语言的指针;-)以及稍微更严格的数学处理。

        3
  •  90
  •   Shea Daniels    16 年前

    下面是一个具体的例子:

    假设你有一个计算作用在物体上的引力的函数。如果你不知道公式,你可以找到它。 here . 此函数接受三个必需的参数作为参数。

    现在,在地球上,你只想计算这个星球上物体的力。在函数语言中,你可以把地球的质量传递给函数,然后对其进行部分求值。你得到的是另一个函数,它只需要两个参数,就能计算出地球上物体的引力。这叫咖喱。

        4
  •  45
  •   Orkhan Alikhanov    6 年前

    Currin是一种可以应用于函数的变换,允许它们采取比以前更少的参数。

    例如,在f*中,你可以这样定义一个函数:

    let f x y z = x + y + z
    

    在这里,函数f取参数x,y和z,并将它们相加,这样:

    f 1 2 3
    

    返回6。

    根据我们的定义,我们可以定义Fr:Cury函数:

    let curry f = fun x -> f x
    

    其中“fun x->f x”是一个lambda函数,等价于c中的x=>f(x)。这个函数输入你想要的函数并返回一个 只接受一个参数 并返回指定的函数,其中第一个参数设置为输入参数。

    用我们前面的例子,我们可以得到F的咖喱:

    let curryf = curry f
    

    然后我们可以执行以下操作:—

    let f1 = curryf 1
    

    这为我们提供了函数f1,它等价于f1 y z=1+y+z。这意味着我们可以做如下:

    f1 2 3
    

    返回6。

    这个过程常常与“部分函数应用”混淆,可以定义如下:

    let papply f x = f x
    

    虽然我们可以将它扩展到一个以上的参数,即:

    let papply2 f x y = f x y
    let papply3 f x y z = f x y z
    etc.
    

    部分应用程序将获取函数和参数,并返回一个需要一个或多个较少参数的函数,如前两个示例所示,该函数直接在标准f函数定义中实现,因此我们可以实现前面的结果,从而:

    let f1 = f 1
    f1 2 3
    

    结果是6。

    总之:

    curring和partial function应用的区别在于:

    curring接受一个函数并提供一个接受单个参数的新函数,然后返回指定的函数,并将其第一个参数设置为该参数。 这允许我们将多个参数的函数表示为一系列单参数函数 . 例子:

    let f x y z = x + y + z
    let curryf = curry f
    let f1 = curryf 1
    let f2 = curryf 2
    f1 2 3
    6
    f2 1 3
    6
    

    部分函数应用程序更直接-它接受一个函数和一个或多个参数,并返回一个函数,其中前n个参数设置为指定的n个参数。例子:

    let f x y z = x + y + z
    let f1 = f 1
    let f2 = f 2
    f1 2 3
    6
    f2 1 3
    6
    
        5
  •  31
  •   Orkhan Alikhanov    6 年前

    它可以是一种使用函数来生成其他函数的方法。

    在javascript中:

    let add = function(x){
      return function(y){ 
       return x + y
      };
    };
    

    我们可以这样称呼它:

    let addTen = add(10);
    

    当这个运行时 10 作为 x ;

    let add = function(10){
      return function(y){
        return 10 + y 
      };
    };
    

    这意味着我们返回这个函数:

    function(y) { 10 + y };
    

    所以当你打电话

     addTen();
    

    你真的在打电话:

     function(y) { 10 + y };
    

    如果你这样做:

     addTen(4)
    

    这和:

    function(4) { 10 + 4} // 14
    

    所以我们的 addTen() 不管我们交什么总是加10。我们可以用同样的方法来制造类似的功能:

    let addTwo = add(2)       // addTwo(); will add two to whatever you pass in
    let addSeventy = add(70)  // ... and so on...
    
        6
  •  28
  •   J D    15 年前

    curried函数是由多个重写的参数组成的函数,它接受第一个参数并返回一个接受第二个参数的函数,依此类推。这允许多个参数的函数部分应用它们的一些初始参数。

        7
  •  6
  •   Orkhan Alikhanov    6 年前

    下面是python中的一个玩具示例:

    >>> from functools import partial as curry
    
    >>> # Original function taking three parameters:
    >>> def display_quote(who, subject, quote):
            print who, 'said regarding', subject + ':'
            print '"' + quote + '"'
    
    
    >>> display_quote("hoohoo", "functional languages",
               "I like Erlang, not sure yet about Haskell.")
    hoohoo said regarding functional languages:
    "I like Erlang, not sure yet about Haskell."
    
    >>> # Let's curry the function to get another that always quotes Alex...
    >>> am_quote = curry(display_quote, "Alex Martelli")
    
    >>> am_quote("currying", "As usual, wikipedia has a nice summary...")
    Alex Martelli said regarding currying:
    "As usual, wikipedia has a nice summary..."
    

    (只需使用via+连接来避免分散非python程序员的注意力。)

    编辑以添加:

    http://docs.python.org/library/functools.html?highlight=partial#functools.partial , 这也显示了python实现这一点的方式中部分对象与函数的区别。

        8
  •  4
  •   Mario Narcis Radoi    10 年前

    如果你明白 partial 你已经走到一半了。理念 部分的 将参数预先应用到函数,并返回只需要剩余参数的新函数。当调用这个新函数时,它包括预加载的参数以及提供给它的任何参数。

    克洛伊尔 + 是一种功能,但要使事情清楚明了:

    (defn add [a b] (+ a b))
    

    你可能知道 inc 函数只需在传递的任何数字上添加1。

    (inc 7) # => 8
    

    让我们自己用 部分的 :

    (def inc (partial add 1))
    

    在这里,我们返回另一个函数,该函数在 add . AS 添加 接受两个新的论点 股份有限公司 函数只需要 b argument——不是以前的2个arguments,因为1已经 部分地 应用。因此 部分的 是一个工具,从中可以创建预设值的新函数。这就是为什么在函数式语言中,函数常常将参数从一般排序到具体排序。这使得重用这样的函数更容易,从中构造其他函数。

    现在想象一下,如果语言足够聪明,能够内省地理解 添加 想要两个理由。当我们给它传递一个参数时,如果函数部分地应用了我们代表我们传递的参数,并且理解到我们可能打算稍后提供另一个参数,那会怎么样?我们可以定义 股份有限公司 不显式使用 部分的 .

    (def inc (add 1)) #partial is implied
    

    这是一些语言的行为方式。当人们希望将函数组合成更大的转换时,它非常有用。这将导致一个传感器。

        9
  •  3
  •   James Black    16 年前

    我发现这篇文章及其引用的文章对更好地理解curring很有用: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

    正如其他人提到的,这只是一种拥有单参数函数的方法。

    这很有用,因为您不必假定将传入多少参数,因此不需要2参数、3参数和4参数函数。

        10
  •  3
  •   Marcus Thornton    7 年前

    咖喱可以简化你的代码。这是使用它的主要原因之一。curring是将接受n个参数的函数转换为只接受一个参数的n个函数的过程。

    原则是使用closure(closure)属性传递传递函数的参数,将它们存储在另一个函数中并将其作为返回值处理,这些函数形成一个链,传递最终参数以完成操作。

    这样做的好处是可以通过一次处理一个参数来简化参数的处理,也可以提高程序的灵活性和可读性。这也使程序更易于管理。同时,将代码分割成更小的片段将使其易于重用。

    例如:

    function curryMinus(x) 
    {
      return function(y) 
      {
        return x - y;
      }
    }
    
    var minus5 = curryMinus(1);
    minus5(3);
    minus5(5);
    

    我也可以…

    var minus7 = curryMinus(7);
    minus7(3);
    minus7(5);
    

    这对于使复杂代码整洁、处理非同步方法等非常有用。

        11
  •  2
  •   catch32    11 年前

    curried函数应用于多个参数列表,而不是 一个。

    这里有一个常规的、非curry函数,它添加了两个int 参数,x和y:

    scala> def plainOldSum(x: Int, y: Int) = x + y
    plainOldSum: (x: Int,y: Int)Int
    scala> plainOldSum(1, 2)
    res4: Int = 3
    

    这里有一个类似的函数。相反 在一个包含两个int参数的列表中,将此函数应用于一个 每个int参数:

    scala> def curriedSum(x: Int)(y: Int) = x + y
    curriedSum: (x: Int)(y: Int)Intscala> second(2)
    res6: Int = 3
    scala> curriedSum(1)(2)
    res5: Int = 3
    

    这里发生的是当您调用 curriedSum ,实际上您可以背对背地调用两个传统函数。第一个函数 调用接受一个名为 x ,并返回函数 第二个函数的值。第二个函数接受int参数 y .

    下面是一个名为 first 在精神上就像第一个传统的 函数调用 咖喱和 会这样做:

    scala> def first(x: Int) = (y: Int) => x + y
    first: (x: Int)(Int) => Int
    

    将1应用于第一个函数——换句话说,调用第一个函数 传递1得到第二个函数:

    scala> val second = first(1)
    second: (Int) => Int = <function1>
    

    对第二个函数应用2将得到结果:

    scala> second(2)
    res6: Int = 3
    
        12
  •  2
  •   S2dent    9 年前

    curring的一个例子是,当使用函数时,您目前只知道其中一个参数:

    例如:

    func aFunction(str: String) {
        let callback = callback(str) // signature now is `NSData -> ()`
        performAsyncRequest(callback)
    }
    
    func callback(str: String, data: NSData) {
        // Callback code
    }
    
    func performAsyncRequest(callback: NSData -> ()) {
        // Async code that will call callback with NSData as parameter
    }
    

    这里,因为在将回调发送到时不知道回调的第二个参数 performAsyncRequest(_:) 必须创建另一个lambda/闭包才能将该闭包发送到函数。

        13
  •  2
  •   user3804449    7 年前

    正如所有其他答案一样,curring有助于创建部分应用的函数。javascript不支持自动套现。因此,上面提供的示例在实际编码中可能没有帮助。livescript中有一些很好的例子(基本上编译成js) http://livescript.net/

    times = (x, y) --> x * y
    times 2, 3       #=> 6 (normal use works as expected)
    double = times 2
    double 5         #=> 10
    

    在上面的例子中,当您给出的参数较少时,livescript会为您生成新的curried函数(double)

        14
  •  0
  •   V. S.    7 年前

    在这里,您可以找到用c_实现的简单解释。在评论中,我试图展示咖喱是多么有用:

    public static class FuncExtensions {
        public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
        {
            return x1 => x2 => func(x1, x2);
        }
    }
    
    //Usage
    var add = new Func<int, int, int>((x, y) => x + y).Curry();
    var func = add(1);
    
    //Obtaining the next parameter here, calling later the func with next parameter.
    //Or you can prepare some base calculations at the previous step and then
    //use the result of those calculations when calling the func multiple times 
    //with different input parameters.
    
    int result = func(1);
    
        15
  •  0
  •   MidhunKrishna    6 年前

    curring是将一个函数从callable转换为 f(a, b, c) 变成可调用的 f(a)(b)(c) .

    否则,curring就是将一个接受多个参数的函数分解为一系列接受部分参数的函数。

    字面上讲,curring是一种函数转换:从一种调用方式转换到另一种调用方式。在javascript中,我们通常制作一个包装器来保存原始函数。

    curring不调用函数。它只是改变了它。

    使curry函数为两个参数函数执行curry。换句话说, curry(f) 两个论点 f(a, b) 把它翻译成 f(a)(b)

    function curry(f) { // curry(f) does the currying transform
      return function(a) {
        return function(b) {
          return f(a, b);
        };
      };
    }
    
    // usage
    function sum(a, b) {
      return a + b;
    }
    
    let carriedSum = curry(sum);
    
    alert( carriedSum(1)(2) ); // 3
    

    正如您所看到的,实现是一系列包装。

    • 结果 curry(func) 是包装器 function(a) .
    • 当它被称为 sum(1) ,参数保存在词法环境中,并返回一个新的包装器。 function(b) .
    • 然后 sum(1)(2) 最后的电话 函数(b) 提供2,并将调用传递给原始多参数和。