如何在函数式语言中使用闭包

时间:2010-01-14 20:33:07

标签: functional-programming closures

出于某种原因,我倾向于将闭包与函数语言联系起来。我相信这主要是因为我所看到的关于闭包的讨论几乎总是在一个专注于函数式编程的环境中。话虽这么说,我能想到的闭包的实际用途本质上都是非功能性的。

在函数式语言中是否有闭包的实际用法,或者在我的脑海中是否存在关联,主要是因为闭包用于以函数式编程语言(一流函数,currying等)常见的样式编程?

编辑:我应该澄清一下,我指的是实际的函数式语言,这意味着我正在寻找保留参照透明度的用法(对于相同的输入,你得到相同的输出)。

编辑:添加到目前为止发布内容的摘要:

  1. 闭包用于实现部分评估。具体来说,对于带有两个参数的函数,可以使用一个参数调用它,这会导致它返回一个带有一个参数的函数。通常,第二个函数“存储”传递给它的第一个值的方法是闭包。
  2. 可以使用闭包实现对象。返回一个关闭了许多变量的函数,然后可以像对象属性一样使用它们。函数本身可以返回更多方法,这些方法充当对象方法,也可以访问这些变量。假设未修改变量,则保持参照透明度。

4 个答案:

答案 0 :(得分:6)

我在Javascript代码中使用了很多闭包(这是一种非常实用的语言 - 我开玩笑说它是带有C服装的Scheme)。它们提供了对函数私有的数据封装。

最无处不在的例子:

var generateId = function() {
    var id = 0;
    return function() {
        return id++;
    }
}();
window.alert(generateId());
window.alert(generateId());

但那是Javascript闭包的 hello,world 。然而,还有更多实际用途。

最近,在我的工作中,我需要用滑块编写一个简单的照片库。它的确如下:

var slide = function() {
    var photoSize = ...
    var ... // lots of calculations of sizes, distances to scroll, etc
    var scroll = function(direction, amout) {
        // here we use some of the variables defined just above
        // (it will be returned, therefore it is a closure)
    };
    return {
        up: function() { scroll(1, photoSize); },
        down: function() { scroll(-1, photoSize); }
    }
}();

slide.up();
// actually the line above would have to be associated to some
// event handler to be useful

在这种情况下,我使用了闭包来隐藏所有向上向下滚动逻辑,并且有一个非常语义的代码:在Javascript中,“向上滑动“你会写slide.up()

答案 1 :(得分:3)

闭包的一个很好的用途是构建决策树之类的东西。您返回一个classify()函数,该函数测试是否向左或向下树,然后根据输入数据调用其leftClassify()或rightClassify()函数。叶函数只返回一个类标签。我之前实际上已经用Python和D实现了决策树。

答案 2 :(得分:3)

它们被用于很多事情。以功能组成为例:

let compose f g = fun x -> f (g x)

这将返回一个闭包,该闭包使用创建它的函数环境中的参数。像OCaml和Haskell这样的函数语言实际上隐含地使用了闭包。例如:

let flip f a b = f b a

通常,这将被称为类似let minusOne = flip (-) 1的东西,以创建一个将从其参数中减去1的函数。这种“部分应用”功能实际上与执行此操作相同:

let flip f a = fun b -> f b a

它返回一个闭包,它记住你传入的两个参数并接受另一个自己的参数。

答案 3 :(得分:1)

Closures可用于模拟可响应消息并维护其自身本地状态的对象。这是Scheme中的一个简单的计数器对象:

;; counter.ss
;; A simple counter that can respond to the messages
;; 'next and 'reset.  

(define (create-counter start-from)
  (let ((value start-from))
    (lambda (message)
      (case message
    ((next) (set! value (add1 value)) value)
    ((reset) (set! value start-from))
    (else (error "Invalid message!"))))))

样本用法:

> (load "counter.ss")
> (define count-from-5 (create-counter 5))
> (define count-from-0 (create-counter 0))
> (count-from-5 'next)
6
> (count-from-5 'next)
7
> (count-from-0 'next)
1
> (count-from-0 'next)
2
> (count-from-0 'reset)
> (count-from-0 'next)
1