有没有办法在球拍中看到 lambda 的主体?
Is there a way to see the body of a lambda in Racket?
假设我有这个代码:
#lang racket
(define a
((λ (x) x)
((λ (y) y)
(λ (z)
((λ (w) w) z)))))
我凭直觉知道这个 lambda 表达式(扩展)等于 (λ (z) z)
我的问题是是否有办法打印出 a
的主体,以防我想看看 Racket 在内部简化了多少功能。
更多信息:
默认情况下,如果我在解释器中输入 a
,我会得到 #<procedure:y>
(这似乎暗示了计算发生了多少)。我可以将输出样式更改为 "constructor",比如说,然后结果是 (lambda (a1) ...)
,这更接近我想要的,但我仍然不知道 body 中的内容是重要的部分。
我想如果对 Racket 的评估策略有更透彻的了解就可以回答这个问题,但我仍然对显示过程体是否普遍存在感兴趣。
通常无法在运行时查看闭包主体。
"body" 是源代码的一部分。它被编译成字节码(后来由 JIT 编译成机器码)。闭包包含在运行时为主体中的自由变量捕获的值和指向代码的指针。
也就是说,您可能会对研究 expand
或 compile
的输出感到满意。
现在expand
只扩展了语法,所以您还看不到任何优化:
> (expand-syntax #'(define a
((λ (x) x)
((λ (y) y)
(λ (z)
((λ (w) w) z))))))
(define-values (a)
(#%app (lambda (x) x)
(#%app (lambda (y) y)
(lambda (z)
(#%app (lambda (w) w) z)))))
注意#%app
表示"application"。
要在应用优化后查看结果,我们需要调用编译器。
内置 compile
生成字节码,因此我们使用 compile-zo
将字节码转换为表示字节码的结构(并且它们在 repl 中打印得很好)。
> (compile-zo '((λ (x) x)
((λ (y) y)
(λ (z)
((λ (w) w) z)))))
'#s((compilation-top zo 0)
1
#s((prefix zo 0) 0 () ())
#s((application expr 0 form 0 zo 0)
#s((closure expr 0 form 0 zo 0)
#s((lam expr 0 form 0 zo 0)
()
(preserves-marks single-result)
1
(val)
#f
#()
()
#f
6
#s((localref expr 0 form 0 zo 0) #f 0 #f #f #f))
closure64862)
(#s((closure expr 0 form 0 zo 0)
#s((lam expr 0 form 0 zo 0)
y
(preserves-marks single-result)
1
(val)
#f
#()
()
#f
6
#s((localref expr 0 form 0 zo 0) #f 0 #f #f #f))
y64863))))
只剩一个应用了,所以程序在编译的时候确实简化了。
有关 compile-zo
的定义,请参阅 https://github.com/soegaard/meta/blob/master/runtime/racket-eval.rkt。
最后还有一个选择是查看 JIT 生成的机器代码。
参见 https://github.com/samth/disassemble
更新
如果我没看错字节码,对应的是:
((λ (x) x)
(λ (y) y))
假设我有这个代码:
#lang racket
(define a
((λ (x) x)
((λ (y) y)
(λ (z)
((λ (w) w) z)))))
我凭直觉知道这个 lambda 表达式(扩展)等于 (λ (z) z)
我的问题是是否有办法打印出 a
的主体,以防我想看看 Racket 在内部简化了多少功能。
更多信息:
默认情况下,如果我在解释器中输入 a
,我会得到 #<procedure:y>
(这似乎暗示了计算发生了多少)。我可以将输出样式更改为 "constructor",比如说,然后结果是 (lambda (a1) ...)
,这更接近我想要的,但我仍然不知道 body 中的内容是重要的部分。
我想如果对 Racket 的评估策略有更透彻的了解就可以回答这个问题,但我仍然对显示过程体是否普遍存在感兴趣。
通常无法在运行时查看闭包主体。 "body" 是源代码的一部分。它被编译成字节码(后来由 JIT 编译成机器码)。闭包包含在运行时为主体中的自由变量捕获的值和指向代码的指针。
也就是说,您可能会对研究 expand
或 compile
的输出感到满意。
现在expand
只扩展了语法,所以您还看不到任何优化:
> (expand-syntax #'(define a
((λ (x) x)
((λ (y) y)
(λ (z)
((λ (w) w) z))))))
(define-values (a)
(#%app (lambda (x) x)
(#%app (lambda (y) y)
(lambda (z)
(#%app (lambda (w) w) z)))))
注意#%app
表示"application"。
要在应用优化后查看结果,我们需要调用编译器。
内置 compile
生成字节码,因此我们使用 compile-zo
将字节码转换为表示字节码的结构(并且它们在 repl 中打印得很好)。
> (compile-zo '((λ (x) x)
((λ (y) y)
(λ (z)
((λ (w) w) z)))))
'#s((compilation-top zo 0)
1
#s((prefix zo 0) 0 () ())
#s((application expr 0 form 0 zo 0)
#s((closure expr 0 form 0 zo 0)
#s((lam expr 0 form 0 zo 0)
()
(preserves-marks single-result)
1
(val)
#f
#()
()
#f
6
#s((localref expr 0 form 0 zo 0) #f 0 #f #f #f))
closure64862)
(#s((closure expr 0 form 0 zo 0)
#s((lam expr 0 form 0 zo 0)
y
(preserves-marks single-result)
1
(val)
#f
#()
()
#f
6
#s((localref expr 0 form 0 zo 0) #f 0 #f #f #f))
y64863))))
只剩一个应用了,所以程序在编译的时候确实简化了。
有关 compile-zo
的定义,请参阅 https://github.com/soegaard/meta/blob/master/runtime/racket-eval.rkt。
最后还有一个选择是查看 JIT 生成的机器代码。 参见 https://github.com/samth/disassemble
更新
如果我没看错字节码,对应的是:
((λ (x) x)
(λ (y) y))