是否有用于构建列表的现有lisp宏?

时间:2014-05-28 17:24:20

标签: macros lisp elisp

在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或库中的某处,必须有更好的方法,或者现有的解决方案。

4 个答案:

答案 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)))

无论如何,pushnreverse都没有错。

答案 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)