我有以下常见的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)
感谢您的任何见解.
您不能使用仅接受变量值的函数来修改变量的内容.
采用以下简单示例:
(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出于这些原因,
它本身就是一个宏.
如果你在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)
但是,不要这样做.以功能方式编程.