需要其他语言的break语句或continue语句的代码可以使用Common Lisp和Emacs Lisp中的block & return-from或catch & throw来完成。然后是需要redo语句的代码,或者至少最好用redo编写。而且redo语句不必是关于循环的。我如何在Lisp语言中做redo?
如果在Lisp中有一个等同于redo的东西,我想它应该是这样工作的:接受符号和表单的特殊形式with-redo,以及接受符号的redo。form (with-redo 'foo BODY-FORMS...)可以在其BODY-FORMS中包含(redo 'foo),并且(redo 'foo)将控制权转移回BODY-FORMS的开头。
发布于 2013-06-28 17:06:35
在Common Lisp中:
(tagbody
start
(do-something)
(go start))
(dotimes (i some-list)
redo
(when (some-condition-p)
(go redo))
(some-more))发布于 2013-06-29 17:57:35
更新:Emacs24.4(即将发布)有标记体。Emacs24.4附带的cl-lib包括cl-tagbody.
对于没有标记体的Lisp方言,只要该方言有一个捕获/抛出等效项,就仍然可以实现redo。
对于Emacs Lisp:
;; with-redo version 0.1
(defmacro with-redo (tag &rest body)
"Eval BODY allowing jumps using `throw'.
TAG is evalled to get the tag to use; it must not be nil.
Then the BODY is executed.
Within BODY, a call to `throw' with the same TAG and a non-nil VALUE causes a jump to the beginning of BODY.
A call to `throw' with the same TAG and nil as VALUE exits BODY and this `with-redo'.
If no throw happens, `with-redo' returns the value of the last BODY form."
(declare (indent 1))
(let ((ret (make-symbol "retval")))
`(let (,ret)
(while
(catch ,tag
(setq ,ret (progn ,@body))
nil))
,ret)))
(defun redo (symbol)
(throw symbol t))使用示例(所有示例都用Emacs Lisp编写):
(with-redo 'question
(let ((name (read-string "What is your name? ")))
(when (equal name "")
(message "Zero length input. Please try again.")
(beep)
(sit-for 1)
(redo 'question))
name))以中间测试循环的形式编写相同的示例:
(require 'cl-lib)
(let (name)
(cl-loop do
(setq name (read-string "What is your name? "))
while
(equal name "")
do
(message "Zero length input. Please try again.")
(beep)
(sit-for 1))
name)同样的例子写成了一个无限循环,但却抛出了:
(let (name)
(catch 'question
(while t
(setq name (read-string "What is your name? "))
(unless (equal name "")
(throw 'question name))
(message "Zero length input. Please try again.")
(beep)
(sit-for 1))))实现with-lex-redo-anon和lex-redo,其中(lex-redo)会跳转到文本/词法上最内层的with-lex-redo-anon表单的正文开头:
;; with-lex-redo-anon version 0.1
(require 'cl-lib)
(defmacro with-lex-redo-anon (&rest body)
"Use with `(lex-redo)'."
(let ((tag (make-symbol "lex-redo-tag"))
(ret (make-symbol "retval")))
`(cl-macrolet ((lex-redo () '(cl-return-from ,tag t)))
(let (,ret)
(while
(cl-block ,tag
(setq ,ret (progn ,@body))
nil))
,ret))))示例测试:
(let ((i 0) (j 0))
(with-lex-redo-anon
(with-lex-redo-anon
(print (list i j))
(when (< j 2)
(incf j)
(lex-redo)))
(when (< i 2)
(incf i)
(lex-redo))))与另一个答案中的输出相同。
https://stackoverflow.com/questions/17360847
复制相似问题