Francis King 的answer 很清楚,
这只是一个受其启发的扩展脚注。
用助记符替换标识符(将q重写为square)可以更容易理解代码[1]
Procedures as [first-class] values in Scheme[2] 经常用lambda的例子来介绍:
> (define twelve 12)
> (define square (lambda (x) (* x x)))
> (square twelve)
144
>
正如上面代码中的字符12 是一个数字的表示,
字符(lambda (x) (* x x)) 是过程的表示:
(number? 12) => #t, (procedure? (lambda (x) (* x x))) => #t
另外两个代码重写可能会有所帮助:
对过程使用“短格式”define,并注释定义
带有类型签名(参数和结果类型):
> (define (square x) (* x x)) ;; Number -> Number
> (square 3)
9
> (define (do-twice f x) ;; (X -> X) X -> X
(f (f x)))
> (do-twice square 3)
81
> (map (lambda (x) (do-twice square x))
'(1 2 3))
(1 16 81)
>
请注意,此do-twice 尚未对应问题的p:
这两次 map 的第一个参数需要:
(map do-twice (make-list 3 square) '(1 2 3))
映射一个列表需要一个参数的函数,所以某些东西必须产生两次
(define (do-twice x) (f (f x))) 作为它的值:
> (define (do-twice-with f) ;; (X -> X) -> (X -> X)
(define (do-twice x) ;; X -> X
(f (f x)))
do-twice)
> ((do-twice-with square) 3)
81
> (map (do-twice-with square)
'(1 2 3))
(1 16 81)
>
所以do-twice-with 是问题中的函数p。
do-twice-with 需要一个函数参数 (X -> X),但 X 可以是任何类型,所以:
> (define (repeat s) ;; String -> String
(string-append s " " s))
> ((do-twice-with repeat) "buffalo")
"buffalo buffalo buffalo buffalo"
而do-twice-with 本身具有类型 (X' -> X')(其中 X' 代表 (X -> X)),所以可以
应用于自身:
> (((do-twice-with do-twice-with) square) 3)
43046721
> (((do-twice-with do-twice-with) repeat) "buffalo") [3]
"buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo"
((((do-twice-with do-twice-with) do-twice-with) square) 3) 保留为
读者练习...
[1] "Naming is perhaps the most powerful abstracting notion we have" [盖伊 L 斯蒂尔]
[2]https://walker.cs.grinnell.edu/courses/151.sp04/readings/procedures-as-values.xhtml
[3]https://en.wikipedia.org/wiki/Buffalo_buffalo_Buffalo_buffalo_buffalo_buffalo_Buffalo_buffalo