我是Scheme和Lisp的新手,在学习之后我偶然发现了本地程序绑定中使用的神秘语法:
(define mock
(lambda (s)
;; this is what I don't understand
(let splice ([l '()] [m (car s)] [r (cdr s)])
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))))
我花了一段时间才意识到splice
是一个有3个arities的范围程序。以ML-esque样式重写它似乎产生类似的输出:
(define mock2
(lambda (s)
;; define `splice` first
(define splice
(lambda (la lb lc)
(append
(map (lambda (x) (cons lb x)) lc)
(if (null? lc) '()
(splice (cons lb la) (car lc) (cdr lc))))))
;; bind `splice` and its arguments together and call it with them
(let ([sp splice] [l '()] [m (car s)] [r (cdr s)])
(splice l m r))))
第二个版本有点长,看起来更有必要,但是在将splice
定义为与参数并行绑定(或者只是按原样查看)并调用之前,将l
定义为范围内的正常过程看起来很健康。
问题是这两个版本可以替换吗?如果是,您能帮助解释第一版本在m
绑定表单中绑定局部变量(r
,splice
和 memberr(X,[X|_]):-
!.
memberr(X,[_H|T]):-
memberr(X,T).
lengthOfList(List,R):-
lengthOfList(List,0,R).
lengthOfList([],L,L).
lengthOfList([_|Tail],Acc,R):-
NewAcc is Acc + 1,
lengthOfList(Tail,NewAcc,R).
remove_second([],[]).
remove_second([H|T],R):-
remove_second(H,T,R,[]).
remove_second(H,[],[H|[]],List):-
integer(H),
\+ memberr(H,List).
remove_second(H,[],[],List):-
integer(H),
memberr(H,List).
remove_second(Elem,[H2|Tail2],[Elem|R],List):-
integer(Elem),
(
\+ memberr(Elem,List) ->
remove_second(H2,Tail2,R,[Elem|List])
).
remove_second(Elem,[H2|Tail2],R,List):-
integer(Elem),
(
memberr(Elem,List) ->
remove_second(H2,Tail2,R,List)
).
remove_second(Elem,[H2|Tail],[P|R],List):-
(
\+ integer(Elem) ->
lengthOfList(Elem,L),
L > 0,
remove_second(Elem,P),
remove_second(H2,Tail,R,List)
).
remove_second(Elem,[H2|Tail],[[]|R],List):-
(
\+ integer(Elem) ->
lengthOfList(Elem,L),
L == 0,
remove_second(H2,Tail,R,List)
).
remove_second(Elem,[],[[]],_):-
(
\+ integer(Elem) ->
lengthOfList(Elem,L),
L == 0
).
remove_second(Elem,[],[P],_):-
(
\+ integer(Elem) ->
lengthOfList(Elem,L),
L > 0,
remove_second(Elem,P)
).
)的语法吗?
答案 0 :(得分:2)
调用splice
就像重新进入一个循环,这就是它的用途。无论如何,尾部调用是一个goto。它通常被命名为loop
,而不是为它考虑一些特殊名称。
“看起来更健康”是值得商榷的,实际上对于Schemers你会失去这个,因为这是一个非常流行的Scheme结构,名为“named let”。它通常用letrec
btw重写,如果/当想要重写它时,更好地理解它。内部define
也可以使用,但是,为什么不首先使用(define (mock s) ...
。
所以,通常的方法是重写这个
(define mock ; or: (define (mock s) ...
(lambda (s)
(let splice ([l '()] [m (car s)] [r (cdr s)])
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))))
就是这样:
(define mock
(lambda (s)
(letrec ([splice (lambda (l m r) ; or: (define (splice l m r) ...
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))])
(splice '() (car s) (cdr s)))))
并以名为let 的方式编写它可以节省一个在一个地方定义它并在另一个地方调用,可能很远。无论如何,一个呼叫从一开始就进入它的身体,并命名为更好地反映那个。
这是非常不言自明的。从一种形式到另一种形式的转换纯粹是语法上的,两者都可以互换使用。