我正在通过书籍Realm of Racket
学习球拍。我注意到定义变量和定义函数是通过相同的关键字 - define
完成的。这让我想到了一个不带参数的函数和简单地返回值5
之间的根本区别,以及一个返回值5
的变量。即:
(define fiveVar 5)
(define (fiveFun)
5)
(道歉,如果骆驼案不是正确的命名惯例 - 只是拿起书并且不知道任何Lisp)。
乍一看,在我看来,在两种情况下,我只是给值5
命名。事实上,我甚至可以使用变量重新附加名称fiveFun
:(set fiveFun 6)
。
再次,在我看来,定义变量和定义函数之间没有区别。在这两种情况下,我都给数据类型命名。在函数数据类型的情况下,可以应用它,而数字则不能。
答案 0 :(得分:7)
差不多,是的。事实上,(define (f x ...) ...)
表示法只是Scheme和其他Lisp-1中的一个方便的缩写,与Common Lisp和其他Lisp-2形成鲜明对比,后者将函数和值分成不同的名称空间。
在Racket和Scheme中,当你写这个:
(define (f x) (+ x 1))
这实际上只是写这个的简写符号:
(define f (lambda (x) (+ x 1)))
lambda
表单是一个生成过程值的特殊表单,但是创建的绑定(在本例中为f
)与绑定没有区别一个普通的价值。由于过程只是与其他过程一样的值,因此使用所谓的“高阶”函数 - 接受函数作为参数的函数 - 易于编写和使用。例如,filter
将函数作为其第一个参数:
> (filter even? '(1 2 3 4))
'(2 4)
现在,考虑到这一点,你在问题中写的函数定义实际上就是这样,没有简写:
(define fiveFun (lambda () 5))
那么,考虑一下这两件事之间的区别:
(define fiveVar 5)
(define fiveFun (lambda () 5))
好吧,其中一个是数字5
,另一个是没有参数的函数,当应用时,生成 5.有些语言,大部分都是纯函数,甚至没有一个不带参数的函数的概念,因此没有有意义的方法来表达这些语言中的fiveFun
。但是,在Racket中,有几个原因可能需要这样的功能:
您可以使用无参数函数将评估“延迟”到另一个时间,甚至多次运行,这在运行该函数时会产生副作用。例如:
> (define x (begin (displayln "Hello!") 5))
Hello!
> x
5
> x
5
> (define f (lambda () (displayln "Hello!") 5))
> f
#<procedure:f>
> (f)
Hello!
5
> (f)
Hello!
5
此外,值可能需要很长的时间才能生成,您可能希望将其评估推迟到实际需要时。当实际使用该函数时,您可以使用零参数函数来“懒惰地”运行计算。
作为一小段术语,零参数函数通常被称为“thunk”,您可能会在Racket文档中的某些地方看到它。甚至还有一个来自thunk
的名为racket/function
的表单,它是零参数lambda的简写。