lisp - 存储函数调用并执行它们

标签 lisp common-lisp

我编写了这些函数来混合给定列表:

(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可以写吗?

最佳答案

在您的第一次尝试中,有些事情有点奇怪。您返回一个函数,如果 operations 尚未填充,它将创建与调用该函数的列表中一样多的交换。但是您可以在任何列表上调用该函数,即使它的长度不同。如果你用一个长列表调用它,然后传入一个较短的列表,你将尝试交换列表实际上没有的位置。另外,你不需要写swap; Common Lisp 已经提供了一个更通用的 rotatef ,它接受任意位置,例如 nth。所以,我会把它写成一个函数,它接受一个列表,然后返回一个函数,该函数的连续调用返回该列表的随机版本。避免您展示的那种eval 用法的技巧是收集lambda 函数而不是代码blob,然后函数调用 它们。

(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)

当然,创建交换函数列表也有很多选项。但是,请注意使用捕获循环 变量,因为它们可能不会在每次迭代时重新启动。 (这就是我在 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)))

此外,作为对您评论的回应,请注意 ma​​ke-shuffler 函数返回的列表始终是相同的列表,只是其元素的顺序不同。这意味着您可以在调用 shuffler 函数之间修改列表,并且您会看到反射(reflect)的更改(但是这是有道理的)。例如:

(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

关于lisp - 存储函数调用并执行它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33637371/

相关文章:

common-lisp - 如何获得common lisp中使用的编译器?

rest - Common Lisp 中的具象状态转移 (REST)

sql-server - 使用集成身份验证将 Windows 上的 SBCL 连接到 SQL Server

lisp - 为什么CLOS slot可以解绑?

emacs - 重新编译不会更正函数调用中的拼写错误

lisp - 普通口齿不清 : unable to get the uncompress function in Paul Graham's book working

scope - Common Lisp (SBCL) 中的动态变量闭包

lisp - 如何使这个邻居功能?

lisp - 让里面条件

io - C printf 打印的文本在哪里