就是元循环求值器,你可以参考:怎样写一个解释器
喔,原来你卡在这里啊。
nameless representation of terms 对初学者确实比较麻烦,主要是实现细节比较多(我记得SICP里也有讲到过它,虽然不是用在同一个地方。。。)
注:nameless representation of terms本质上只是一种优化策略,它并不是这本书的核心(实际上,你使用改名的方法也可以实现同样的目的),建议不要过多的浪费时间在上面。
就是元循环求值器,你可以参考:怎样写一个解释器
喔,原来你卡在这里啊。
nameless representation of terms 对初学者确实比较麻烦,主要是实现细节比较多(我记得SICP里也有讲到过它,虽然不是用在同一个地方。。。)
注:nameless representation of terms本质上只是一种优化策略,它并不是这本书的核心(实际上,你使用改名的方法也可以实现同样的目的),建议不要过多的浪费时间在上面。
这个挺有意思,我可以用Emacs Lisp实现一个。
好滴。
没用过 OCaml 是没法理解为什么有些地方要这么设计的。更何况这书是用 OCaml 写 OCaml,不像 Scheme 写 Scheme 顶破天也就那么复杂,要求事先熟悉 OCaml 是很合理的
我觉得能写几道 Project Euler 已经是很低的要求了。再低就是要求写 hello world 了。以 Helium 教学编译器提供的没有 typeclass 的 Haskell98 而论完全掌握真的是很简单了,不比 R6RS 复杂多少。
那刷完我给的那个合集就有同等水平。
我觉得吧,至少写个R5RS的解释器不需要懂这么多东西,会C,看完SICP和TSPL就行。
你说的对 会了C语言 再看懂数据结构与算法 你就能做很多事情了
这个比TAPL简单多了。我的实现跟他的应该差不多。
(require 'pcase)
(require 'cl-lib)
(defun calc (exp)
(pcase exp
(`(,op ,e1 ,e2)
(let ((r1 (calc e1))
(r2 (calc e2)))
(pcase op
('+ (+ r1 r2))
('- (- r1 r2))
('* (* r1 r2))
('/ (/ r1 r2)))))
((pred numberp) exp)))
(defun ext-env (bind env)
(cons bind env))
(defun ext-envs (bind-list env)
(append bind-list env))
(defun lookup (var env)
(cl-loop for bind in env
for vr = (car bind)
for va = (cdr bind)
if (eq var vr)
return va
finally return nil))
;; (closure f env)
;; (lambda (x) body)
;; (let (var val) body)
;; (function arg)
(defun interp (exp env)
(pcase exp
;; variable
((pred (lambda (e) (and (symbolp e) (not (booleanp e)))))
(interp (lookup exp env) env))
;; single lambda / closure
(`(lambda . ,_) `(closure ,exp ,env))
(`(closure . ,_) exp)
;; lambda / closure application
(`((lambda . ,rest) ,arg)
(interp (list (interp `(lambda . ,rest) env) (interp arg env)) env))
(`((closure (lambda (,x) ,body) ,e) ,arg)
(interp body (ext-env (cons x (interp arg (ext-envs e env)))
(ext-envs e env))))
;; symbol application
(`(,f ,a) (when (and (symbolp f) (not (booleanp f)))
(interp `(,(interp f env) ,(interp a env)) env)))
;; let
(`(let (,var ,val) ,body)
(interp body (ext-env (cons var (interp val env)) env)))
;; arithmetic
;; op e1 e2 can be confused with let bind body
(`(+ ,e1 ,e2) (+ (interp e1 env) (interp e2 env)))
(`(- ,e1 ,e2) (- (interp e1 env) (interp e2 env)))
(`(* ,e1 ,e2) (* (interp e1 env) (interp e2 env)))
(`(/ ,e1 ,e2) (/ (interp e1 env) (interp e2 env)))
;; number
((pred numberp) exp)))
(interp '(let (x (lambda (x) (x 1)))
(x (lambda (x) (+ 1 x))))
nil)
;;=> 2
(学Haskell去了
我觉得macro玩得熟练的话,和写解释器是差不多的
其实这本书把所有关于实现的章节都跳过也没什么问题吧。Chapter dependencies 那张图里把4,6,7,10,17,25 都扣掉以后还是 self-contained。(当然这么说有点不负责任,毕竟我只看过几章。)
macro (expender) 和 interpreter 差别还是挺大的吧
对于lisp系,我个人感觉是一样的
Ok,我试试
纠正一个错误,正确的说法是:
任意群G和G上的一个对称群的子群同构
或者说 任意群与某一个置换群同构
这个叫做凯莱定理( Cayley’s theorem ),它可以被看成米田引理(Yoneda lemma)在群论上的推广。
群是它自身的子群,所以没什么错误。
这两天放假,又开始看TAPL。知乎上说最好自己实现一遍lambda calculus的解释器,我就又看了一遍前几章,先用Emacs Lisp实现了一遍。这样之后用Haskell只要照着实现就好了:
感觉挺酷的www。试了几个简单的表达式,感觉没问题,如果有错请告诉我www。
(show (parse '((λ y (λ x x y)) (λ y y))))
;;=> "(λy.λx.xy)(λy.y)"
(show (eval1 (parse '((λ y (λ x x y)) (λ z z)))))
;;=> "λx.x(λz.z)"
P.S. Gist莫名把缩进搞得乱七八糟……
P.P.S. 再打开就又好了……
先练练Haskell