如何在Common Lisp中迭代返回多个值



要返回多个值,可以使用(values v1 v2 v3)

如何从do循环中迭代返回多个值?换句话说,我想在do循环中构建多值返回的值,但我只能构建一个列表。我知道values-list,但使用多个值的目的是不构建列表。

有没有任何方法可以在不构建列表的情况下迭代返回多个值?

如果您的Common Lisp实现支持堆栈分配,那么这将是一种处理方法。

例如,在LispWorks中,这是可能的:

(defun foo (list)
(let ((vlist (make-list (length list)
:initial-element nil)))
(declare (dynamic-extent vlist))              ; allocate VLIST on the stack
(values-list (map-into vlist #'1+ list))))    ; return VLIST as values

以类似的方式,可以使用DO来设置堆栈分配列表。

LispWorks将MAKE-LISTdynamic-extent声明结合起来进行堆栈分配。SBCL没有,但可以使用LIST之类的东西进行堆栈分配。

正如我在一条评论中所说:如果你不知道你想要多少个值,那么你可能不应该使用多个值:由于限制可能低至20,你可能会达到这个限制,任何接收器都需要将值转换成数据结构来知道有多少个值。简单地返回数据结构会更好。

因此,我假设您确实知道您想要多少值。在这种情况下,您可以为每个值绑定一个变量。当然,没有人愿意写这样的代码。好吧,这就是Lisp:你不必写无聊的重复代码!

(defmacro with-indexed-values ((accessor n &optional (initial-value 'nil))
&body decls/forms)
"Evaluate DECLS/FORMS with N indexed values: (ACCESSOR i) will
retrieve the ith value, and (SETF (ACCESSOR I) v) will set it Indices
run from 0.  All of the indexed values are returned as multiple
values.  INITIAL-VALUE, if given is the initial value for the values:
it is evaluated once."
(unless (<= n multiple-values-limit)
(error "~A is too many values: limit is ~A" n multiple-values-limit))

(let ((varnames (loop for i below n
collect (make-symbol (format nil "V~D" i))))
(ivname (make-symbol "IV")))
`(let ((,ivname ,initial-value))
(let ,(loop for v in varnames collect `(,v ,ivname))
(flet ((,accessor (i)
(case i
,@(loop for v in varnames
for i upfrom 0
collect `(,i ,v))
(otherwise
(error "~A isnt an integer in [0, ~A)" i ,n))))
((setf ,accessor) (new i)
(case i
,@(loop for v in varnames
for i upfrom 0
collect `(,i (setf ,v new)))
(otherwise
(error "~A isnt an integer in [0, ~A)" i ,n)))))
(declare (inline ,accessor (setf ,accessor)))
,@decls/forms
(values ,@varnames))))))

现在:

> (with-indexed-values (v 2)
(setf (v 0) 2)
(setf (v 1) (+ (v 0) 1))
(decf (v 0)))
1
3

> (with-indexed-values (v 5 (cons nil nil))
(loop for i below 3
do (setf (v i) i)))
0
1
2
(nil)
(nil)

(最后两个值是eq。(

这是同一宏的另一个版本:这个宏将值存储在向量中,并依赖于堆栈分配的向量。LispWorks可以做到这一点:我没有检查过其他实现。这种方法的优点是访问器只是数组引用,应该很快,但缺点是它依赖于分配向量的实现堆栈。

(defmacro with-indexed-values ((accessor n &optional (initial-value 'nil))
&body decls/forms)
"Evaluate DECLS/FORMS with N indexed values: (ACCESSOR i) will
retrieve the ith value, and (SETF (ACCESSOR I) v) will set it Indices
run from 0.  All of the indexed values are returned as multiple
values.  INITIAL-VALUE, if given is the initial value for the values:
it is evaluated once."
(unless (<= n multiple-values-limit)
(error "~A is too many values: limit is ~A" n multiple-values-limit))
(let ((vname (make-symbol "V")))
`(let ((,vname
(let ((init ,initial-value))
(vector ,@(loop repeat n collect 'init)))))
(declare (dynamic-extent ,vname))
(flet ((,accessor (i)
(aref ,vname i))
((setf ,accessor) (new i)
(setf (aref ,vname i) new)))
(declare (inline ,accessor (setf ,accessor)))
,@decls/forms
(values ,@(loop for i below n
collect `(aref ,vname ,i)))))))

最新更新