存储函数调用并执行它们
Store function calls and execute them
我编写了这些函数来混合给定列表:
(defun swap (lst pos1 pos2)
"Destructively swap values of lst between positions pos1 and pos2"
(let ((aux (nth pos1 lst)))
(setf (nth pos1 lst) (nth pos2 lst))
(setf (nth pos2 lst) aux)) lst)
(defun mix-function ()
"Create function to mix values, and than revert changes"
(let ((parameters '()))
(lambda (lst)
(let ((len (length lst)))
(if (null parameters)
(dotimes (i len)
(push (list (random len) (random len)) parameters))
(setf parameters (nreverse parameters)))
(map nil #'(lambda (x) (apply #'swap (cons lst x))) parameters) lst))))
调用示例:
CG-USER(109): (defparameter fun (mix-function))
FUN
CG-USER(110): (defparameter val '(1 2 3 4 5))
VAL
CG-USER(111): (funcall fun val)
(5 2 1 4 3)
CG-USER(112): (funcall fun val)
(1 2 3 4 5)
是否可以通过存储函数调用来做到这一点?我该怎么做?到目前为止,在第一次函数调用后使用 setf 时,我所拥有的效果不佳:
(defun mix-function2 ()
(let ((operations '()))
(lambda (lst)
(let ((len (length lst)))
(if (null operations)
(dotimes (i len)
(push `(swap ',lst ,(random len) ,(random len)) operations))
(setf operations (nreverse operations)))
(map nil #'eval operations) lst))))
CG-USER(140): (defparameter fun2 (mix-function2))
FUN2
CG-USER(141): val
(1 2 3 4 5)
CG-USER(142): (funcall fun2 val)
(5 2 1 4 3)
CG-USER(143): (setf val '(1 2 3 4 5 6))
(1 2 3 4 5 6)
CG-USER(144): (funcall fun2 val)
(1 2 3 4 5 6)
CG-USER(145): (funcall fun2 val)
(1 2 3 4 5 6)
而且可以不写eval吗?
您的第一次尝试有一些地方有点奇怪。您 return 一个函数,如果 operations 尚未填充,将创建与调用该函数的列表中一样多的交换。但是您可以在任何列表上调用该函数,即使它的长度不同。如果你用一个长列表调用它,然后传入一个较短的列表,你将尝试交换列表实际上没有的位置。另外,你不需要写 swap; Common Lisp 已经提供了一个更通用的 rotatef,它接受任意位置,比如 nth。所以,我会把它写成一个接受列表的函数,然后 returns 一个函数,它的连续调用 return 打乱了那个列表的版本。避免您展示的那种 eval 用法的技巧是收集 lambda 函数而不是代码 blob,然后到 funcall 他们。
(defun make-shuffler (list)
(let* ((len (length list))
(operations (loop repeat len
collect (let ((i (random len))
(j (random len)))
(lambda ()
(rotatef (nth i list) (nth j list)))))))
(lambda ()
(map nil 'funcall operations)
list)))
CL-USER> (defparameter *f* (make-shuffler (list 1 2 3 4 5 6)))
*F*
CL-USER> (funcall *f*)
(4 1 5 2 3 6)
CL-USER> (funcall *f*)
(2 4 3 1 5 6)
CL-USER> (funcall *f*)
(1 2 5 4 3 6)
CL-USER> (funcall *f*)
(4 1 3 2 5 6)
CL-USER> (funcall *f*)
(2 4 5 1 3 6)
CL-USER> (funcall *f*)
(1 2 3 4 5 6)
当然,创建交换函数列表也有很多选项。但是,请注意使用捕获 loop 变量,因为它们可能不会在每次迭代时重新启动。 (这就是我在 collect 子句中使用 let 的原因。)但您也可以只收集随机索引,然后调用单个交换函数.这可能会提高内存效率(但我还没有检查过):
(defun make-shuffler (list)
(let* ((len (length list))
(index-pairs (loop repeat len
collect (list (random len) (random len))))
(swap (lambda (i j)
(rotatef (nth i list) (nth j list)))))
(lambda ()
(map nil (lambda (indices)
(apply swap indices))
index-pairs)
list)))
此外,为了回应您的评论,请注意 make-shuffler 函数 return 编辑的列表始终是同一个列表,只是其元素位于不同的顺序。这意味着您可以在调用 shuffler 函数之间修改列表,并且您会看到反映的更改(但是这是有道理的)。例如:
(let* ((list (list 1 2 3 4 5))
(f (make-shuffler list)))
(funcall f)
(print list)
(setf (second list) 'x)
(setf (third list) 'y)
(print list)
(funcall f)
(print list))
; (3 4 2 1 5) ; after first shuffle
; (3 X Y 1 5) ; after setting X and Y, but without shuffling
; (Y 1 X 3 5) ; after another shuffle, X and Y are still in the list
我编写了这些函数来混合给定列表:
(defun swap (lst pos1 pos2)
"Destructively swap values of lst between positions pos1 and pos2"
(let ((aux (nth pos1 lst)))
(setf (nth pos1 lst) (nth pos2 lst))
(setf (nth pos2 lst) aux)) lst)
(defun mix-function ()
"Create function to mix values, and than revert changes"
(let ((parameters '()))
(lambda (lst)
(let ((len (length lst)))
(if (null parameters)
(dotimes (i len)
(push (list (random len) (random len)) parameters))
(setf parameters (nreverse parameters)))
(map nil #'(lambda (x) (apply #'swap (cons lst x))) parameters) lst))))
调用示例:
CG-USER(109): (defparameter fun (mix-function))
FUN
CG-USER(110): (defparameter val '(1 2 3 4 5))
VAL
CG-USER(111): (funcall fun val)
(5 2 1 4 3)
CG-USER(112): (funcall fun val)
(1 2 3 4 5)
是否可以通过存储函数调用来做到这一点?我该怎么做?到目前为止,在第一次函数调用后使用 setf 时,我所拥有的效果不佳:
(defun mix-function2 ()
(let ((operations '()))
(lambda (lst)
(let ((len (length lst)))
(if (null operations)
(dotimes (i len)
(push `(swap ',lst ,(random len) ,(random len)) operations))
(setf operations (nreverse operations)))
(map nil #'eval operations) lst))))
CG-USER(140): (defparameter fun2 (mix-function2))
FUN2
CG-USER(141): val
(1 2 3 4 5)
CG-USER(142): (funcall fun2 val)
(5 2 1 4 3)
CG-USER(143): (setf val '(1 2 3 4 5 6))
(1 2 3 4 5 6)
CG-USER(144): (funcall fun2 val)
(1 2 3 4 5 6)
CG-USER(145): (funcall fun2 val)
(1 2 3 4 5 6)
而且可以不写eval吗?
您的第一次尝试有一些地方有点奇怪。您 return 一个函数,如果 operations 尚未填充,将创建与调用该函数的列表中一样多的交换。但是您可以在任何列表上调用该函数,即使它的长度不同。如果你用一个长列表调用它,然后传入一个较短的列表,你将尝试交换列表实际上没有的位置。另外,你不需要写 swap; Common Lisp 已经提供了一个更通用的 rotatef,它接受任意位置,比如 nth。所以,我会把它写成一个接受列表的函数,然后 returns 一个函数,它的连续调用 return 打乱了那个列表的版本。避免您展示的那种 eval 用法的技巧是收集 lambda 函数而不是代码 blob,然后到 funcall 他们。
(defun make-shuffler (list)
(let* ((len (length list))
(operations (loop repeat len
collect (let ((i (random len))
(j (random len)))
(lambda ()
(rotatef (nth i list) (nth j list)))))))
(lambda ()
(map nil 'funcall operations)
list)))
CL-USER> (defparameter *f* (make-shuffler (list 1 2 3 4 5 6)))
*F*
CL-USER> (funcall *f*)
(4 1 5 2 3 6)
CL-USER> (funcall *f*)
(2 4 3 1 5 6)
CL-USER> (funcall *f*)
(1 2 5 4 3 6)
CL-USER> (funcall *f*)
(4 1 3 2 5 6)
CL-USER> (funcall *f*)
(2 4 5 1 3 6)
CL-USER> (funcall *f*)
(1 2 3 4 5 6)
当然,创建交换函数列表也有很多选项。但是,请注意使用捕获 loop 变量,因为它们可能不会在每次迭代时重新启动。 (这就是我在 collect 子句中使用 let 的原因。)但您也可以只收集随机索引,然后调用单个交换函数.这可能会提高内存效率(但我还没有检查过):
(defun make-shuffler (list)
(let* ((len (length list))
(index-pairs (loop repeat len
collect (list (random len) (random len))))
(swap (lambda (i j)
(rotatef (nth i list) (nth j list)))))
(lambda ()
(map nil (lambda (indices)
(apply swap indices))
index-pairs)
list)))
此外,为了回应您的评论,请注意 make-shuffler 函数 return 编辑的列表始终是同一个列表,只是其元素位于不同的顺序。这意味着您可以在调用 shuffler 函数之间修改列表,并且您会看到反映的更改(但是这是有道理的)。例如:
(let* ((list (list 1 2 3 4 5))
(f (make-shuffler list)))
(funcall f)
(print list)
(setf (second list) 'x)
(setf (third list) 'y)
(print list)
(funcall f)
(print list))
; (3 4 2 1 5) ; after first shuffle
; (3 X Y 1 5) ; after setting X and Y, but without shuffling
; (Y 1 X 3 5) ; after another shuffle, X and Y are still in the list