在Python中,我可以使用yield
来构建列表,而无需定义临时变量:
def get_chars_skipping_bar(word):
while word:
# Imperative logic which can't be
# replaced with a for loop.
if word[:3] == 'bar':
word = word[3:]
else:
yield foo[0]
foo = foo[1:]
在elisp中,无论是内置还是使用任何预先存在的库,我都看不到这样做的任何方法。我被迫手动建立一个列表并在其上调用nreverse
。由于这是一种常见的模式,我编写了自己的宏:
(require 'dash)
(require 'cl)
(defun replace-calls (form x func)
"Replace all calls to X (a symbol) in FORM,
calling FUNC to generate the replacement."
(--map
(cond
((consp it)
(if (eq (car it) x)
(funcall func it)
(replace-calls it x func)))
(:else it))
form))
(defmacro with-results (&rest body)
"Execute BODY, which may contain forms (yield foo).
Return a list built up from all the values passed to yield."
(let ((results (gensym "results")))
`(let ((,results (list)))
,@(replace-calls body 'yield
(lambda (form) `(push ,(second form) ,results)))
(nreverse ,results))))
使用示例:
(setq foo "barbazbarbarbiz")
(with-results
(while (not (s-equals? "" foo))
;; Imperative logic which can't be replaced with cl-loop's across.
(if (s-starts-with? "bar" foo)
(setq foo (substring foo 3))
(progn
(yield (substring foo 0 1))
(setq foo (substring foo 1))))))
在elisp,cl.el或库中的某处,必须有更好的方法,或者现有的解决方案。
答案 0 :(得分:2)
Python函数实际上是生成器。在ANSI Common Lisp中,我们通常会使用词法闭包来模拟生成器,或者我们使用库直接定义生成器,如Pygen。也许这些方法可以移植到Emacs Lisp。
答案 1 :(得分:2)
AFAIK,人们就像你一样使用push+nreverse
。如果您想以更健壮的方式定义宏(例如,因此它不会像(memq sym '(yield stop next))
那样失误),您可以这样做:
(defmacro with-results (&rest body)
"Execute BODY, which may contain forms (yield EXP).
Return a list built up from all the values passed to `yield'."
(let ((results (gensym "results")))
`(let ((,results '()))
(cl-macrolet ((yield (exp) `(push ,exp ,results)))
,@body)
(nreverse ,results))))
答案 2 :(得分:0)
也许是这样的:
(setq foo "barbaz")
(cl-loop for i from 0 to (1- (length foo))
collect (string (aref foo i)))
无论如何,push
和nreverse
都没有错。
答案 3 :(得分:0)
Lisp与Python不同。不使用产量。我也看到使用类似coroutine的结构作为一个错误。它等同于 come-from 结构。突然例程有多个依赖于上下文的入口点。
在Lisp中使用函数/闭包。
在Common Lisp中,LOOP宏允许对向量进行有效映射。如果愿意,可以将以下代码抽象为某个映射函数:
CL-USER 17 > (defun chars-without-substring (string substring)
(loop with i = 0
while (< i (length string))
when (and (>= (- (length string) i) (length substring))
(string= substring string
:start2 i
:end2 (+ i (length substring))))
do (incf i (length substring))
else
collect (prog1 (char string i) (incf i))))
CHARS-WITHOUT-SUBSTRING
CL-USER 18 > (chars-without-substring "barbazbarbarbiz" "bar")
(#\b #\a #\z #\b #\i #\z)