常见的lisp从功能推送

ema*_*nea 5 lisp common-lisp

我有以下常见的lisp函数:(aggregate line1 line2)(queuer data result).

queuer如果它们的第一个字段不同,或者如果它们的第一个字段相等line1,line2那么它们应该将值与第一个字段不同,或者这两个行的集合.

我不知道为什么它不会改变我的结果列表.

注意:我正在初始化结果列表,其中(push (pop data) result)包含第一个元素.2个列表是1深度嵌套列表(("1" "text") ("2" "text") (...)).

(defun aggregate (line1 line2)
  (progn
    (list 
     (nth 0 line1)
     (nth 1 line1)
     (nth 2 line1)
     (concatenate 'string (nth 3 line1) ", " (nth 3 line2))
     (concatenate 'string (nth 4 line1) ", " (nth 4 line2)))))

(push (pop x) y)

(defun queuer (data result)
  (loop do
       (let ((line1 (pop data))
             (line2 (pop result)))
         (if (equal (first line1) (first line2))
             (progn
               (push (aggregate line1 line2) result)
               (print "=="))
             (progn
               (push line2 result)
               (push line1 result)
               (print "<>"))))
       while data))
Run Code Online (Sandbox Code Playgroud)

感谢您的任何见解.

Sva*_*nte 7

您不能使用仅接受变量值的函数来修改变量的内容.

采用以下简单示例:

(defun futile-push (thing list)
  (push thing list))

(let ((foo (list 1)))
  (futile-push 2 foo))
Run Code Online (Sandbox Code Playgroud)

怎么了?

  • Foo 被评估到它指向的列表.
  • 2 评估为2.
  • 这两个参数传递给函数.

在函数调用内:

  • Thing 现在必然要2.
  • List现在绑定到列表(1).

请注意,列表不知道它也被foo函数外部的变量引用 .

         foo
          |
          v
        ---------
list -> | 1 |NIL|
        ---------
Run Code Online (Sandbox Code Playgroud)
  • Pushlist以这样一种方式修改变量,使其现在绑定到列表(2 1).

请注意,这不会影响foo外部. Foo仍然指向与以前相同的东西.

                     foo
                      |
                      v
        ---------   ---------
list -> | 2 | ----> | 1 |NIL|
        ---------   ---------
Run Code Online (Sandbox Code Playgroud)
  • Futile-push返回push表单的返回值,恰好是新值list.

  • 该返回值永远不会被使用或绑定,因此它会消失.

     foo
      |
      v
    ---------
    | 1 |NIL|
    ---------
    
    Run Code Online (Sandbox Code Playgroud)

做你想做的最简单的方法是返回新值,然后在外面设置变量:

(let ((foo (list 1)))
  (setf foo (not-so-futile-push 2 foo)))
Run Code Online (Sandbox Code Playgroud)

如果您需要在多个位置执行此操作,则可能需要为扩展为setf表单的宏编写宏.请注意,push出于这些原因, 它本身就是一个宏.


Rai*_*wig 6

如果你在Lisp中编写函数,最好是"在函数上"思考.函数获取值并返回值.典型的规则是避免副作用.因此,您的函数应返回结果值,而不是"修改"变量值.

代替:

(defparameter *result* '())

(defun foo (a)
   (push a *result*))
Run Code Online (Sandbox Code Playgroud)

使用:

(defparameter *result* '())

(defun foo (a result)
  (push a result)
  result)

(setf *result* (foo a *result*))
Run Code Online (Sandbox Code Playgroud)

还要注意,aggregate不需要progn.

稍微提前(不要这样做):

如果您有全局列表:

(defparameter *foo* '())
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,你不能像这样:

(defun foo (l)
   (push 1 l))
Run Code Online (Sandbox Code Playgroud)

如果调用foo变量*foo*不变.原因:Lisp不传递变量引用,它传递变量的值.

但是我们如何传递参考?好吧,传递一个引用:一个cons单元格会这样做(或者一个结构,一个向量,一个CLOS对象,......):

CL-USER 38 > (defparameter *foo* (list '()))
*FOO*

CL-USER 39 > (defun foo (ref)
               (push 1 (first ref)))
FOO

CL-USER 40 > (foo *foo*)
(1)

CL-USER 41 > (foo *foo*)
(1 1)
Run Code Online (Sandbox Code Playgroud)

现在,如果我们看一下*foo*,它就会改变.但我们并没有真正改变变量.我们已经更改了列表的第一个条目.

CL-USER 42 > *foo*
((1 1))
Run Code Online (Sandbox Code Playgroud)

但是,不要这样做.以功能方式编程.