【问题标题】:Does JavaScript support partial function application?JavaScript 是否支持部分函数应用?
【发布时间】:2014-01-23 22:42:48
【问题描述】:

通读关于 First-Class 函数的 Wikipedia 文章,有一个很好的关于函数式编程各个方面的语言支持表:http://en.wikipedia.org/wiki/First-class_function#Language_support

JavaScript 被列为不具有部分功能的应用程序。但是,有一些技术可以创建一个函数,该函数返回一个函数,其中一些参数存储在闭包中,即:

var add = function(a, b){
    return a + b;
},
apply = function(fn, a){
    return function(b){ 
        return fn(a, b); 
    }
},
addFive = apply(add, 5);

console.log(addFive(2)); // prints 7

这不是偏函数应用吗?如果没有,有人可以提供另一种语言的部分函数应用示例并解释它的不同之处吗?

谢谢!

【问题讨论】:

    标签: javascript functional-programming


    【解决方案1】:
    var func1 = function(a, b) {
      return a + b;
    }
    
    var func2 = func1.bind(undefined, 3);
    
    func2(1); // 4
    func2(2); // 5
    func2(3); // 6
    

    查看docs at developer.mozilla.org

    【讨论】:

    • 这似乎是最直接的答案,因为 bind 现在是该语言的原生语言。
    【解决方案2】:

    您应该查阅以了解这一点的概念称为柯里化(在 Haskell B. Curry 之后)。 n+1个参数的函数和一个参数返回n个参数的函数之间存在同构。如果你递归地应用这个,你可以编写一个任意数量参数的函数作为函数来函数到函数。

    这就是为什么函数式语言中的函数通常被键入为X -> Y -> Z 意思是,这是一个接受X 并返回一个接受Y 的函数返回一个Z 的函数。此签名还意味着您可以只提供一个X,该函数将返回一个函数本身。

    在 Javascript 中,两个参数的函数将具有签名 X * Y -> Z,这意味着它是一个采用一对 X * Y 并返回 Z 的函数。但是,您不能提供半对。

    有两种方法:

    • 始终手动 curry 您的函数。你的add 函数可以写成:

      var curryadd = function(a){
          return function(b){ return a + b; }
      };
      

    有了这个,你现在有了一个函数,它具有部分函数应用所需的实际签名 Int -> Int -> Int。但是,您还必须确保将此函数称为 curryadd(5)(10),这是不自然的做法。

    • 提供高阶函数,它会柯化你的函数。在您的情况下,apply 做了两件事,它将您的 add 函数库化并绑定参数。这可以分为两部分:

      var curry = function(fn) {
          return function(a){ return function(b){ return fn(a,b); } }
      };
      

    这实际上将实现以对作为参数的函数和返回函数的函数之间的同构。还有一种写uncurry 的方法,它倒着做同样的事情。

    因为这一切都必须手动完成,并且没有直接的语言支持,所以说javascript没有部分功能应用(并不是说不能添加到语言中)。

    【讨论】:

    • 我认为您在内部函数中忘记了一些 return 语句。
    • @ThiefMaster:是的,我后来注意到了,但没有互联网连接来改变它。我猜你有时会忘记它们,如果你已经习惯了不需要它们的函数式语言。
    【解决方案3】:

    您展示的是高阶函数、将函数作为参数的函数和/或返回函数的示例。

    部分应用是不同的。 Here a Haskell example:

    add     :: Int -> Int -> Int
    add x y = x + y
    
    addOne = add 1
    

    add 是一个函数,它接受两个Int 并返回一个Int,表示为Int -> Int -> Int。如果您不熟悉语法,在 Javascript 中大致如下所示:

    /**
     * @param int x
     * @param int y
     * @return int
     */
    function add(x, y) {
        return x + y;
    }
    

    add 1 只用一个参数调用此函数,它返回一个新函数,该函数接受一个Int 并返回一个Int (Int -> Int)。 add 函数并未明确设计为高阶函数,它只是部分应用。

    【讨论】:

    • 请原谅,我不熟悉 Haskell 语法。 add 的函数签名是否说“add 需要两个 Int 并返回一个 Int”?这是说调用一个只有 1 个参数的函数会隐式返回部分应用的函数吗?
    • 翻译成 Javascript。是的,简单来说,Int -> Int -> Int 意味着它需要两个 Int 并返回一个。也许更正确,它会将一个Int 和另一个Int 转换为另一个Int。我想,真正的 Haskellers 仍然会对我的术语有疑问,但对于外行来说,这应该可以。 :o)
    • @Thief 有点像,但实际上并不是“add 那样做”。它在 Haskell 评估事物的方式中根深蒂固,无法在 Javascript 中真正重现。
    • 那么术语“部分功能应用”是指允许任何功能具有该特性的语言特性吗?那么,arity 是如何工作的呢?参数是否严格从左到右应用?
    • @deceze:当然可以复制。即使在 Javascript 中,也很有可能手动 curry 你的函数。然而,这将意味着编写和使用你的函数变得非常不自然。请参阅下面的答案。
    【解决方案4】:

    正如其他答案中所述,您所描述的是柯里化;如您所见,它是一种部分应用形式。

    但是,如果你想要的只是部分应用,你可以使用 underscore.js,它增加了很多函数式编程实用程序:http://documentcloud.github.com/underscore/

    【讨论】:

    • 感谢您的额外回答。在我的 JavaScript 编程中,我使用下划线和相当多的函数式编程技术。我只是想澄清维基百科文章中“部分功能应用程序”的含义。我现在看到这是一个本地语言支持的问题,而不是必须通过高阶函数来实现它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    相关资源
    最近更新 更多