公共lisp推送自函数
我有以下常见的lisp函数:公共lisp推送自函数,lisp,common-lisp,Lisp,Common Lisp,我有以下常见的lisp函数:(聚合line1 line2)和(队列数据结果) queuer如果第一个字段不同,则应将值line1和line2推入结果,如果第一个字段相等,则应将这两行的总和推入结果 我不知道为什么它没有改变我的结果列表 注意:我正在用一个(push(pop数据)结果)初始化结果列表,以使第一个元素在那里。这两个列表是1深度嵌套列表(“1”文本)(“2”文本)(…) (定义聚合(第1行第2行) (项目 (名单 (第n 0行1) (第N 1行1) (第2行第1行) (连接“字符串(第
(聚合line1 line2)
和(队列数据结果)
queuer
如果第一个字段不同,则应将值line1
和line2
推入结果,如果第一个字段相等,则应将这两行的总和推入结果
我不知道为什么它没有改变我的结果列表
注意:我正在用一个(push(pop数据)结果)
初始化结果列表,以使第一个元素在那里。这两个列表是1深度嵌套列表(“1”文本)(“2”文本)(…)
(定义聚合(第1行第2行)
(项目
(名单
(第n 0行1)
(第N 1行1)
(第2行第1行)
(连接“字符串(第三行1)”,“(第三行2))
(连接“字符串(第4行1)”,“(第4行2‘‘‘‘‘)’))
(推动(弹出x)y)
(取消排队器(数据结果)
(循环做
(let((第1行(pop数据))
(第2行(弹出结果)))
(如果(相等(第一行1)(第一行2))
(项目
(推送(聚合行1行2)结果)
(打印“=”)
(项目
(推线2结果)
(推送line1结果)
(打印“))
而(数据)
感谢您的帮助。当您调用push-in-queuer时,这会更改绑定“result”的值,而不是result指向的cons单元格
(push x list)
本质上相当于:
(setq list (cons x list))
只要你的队列函数是一个函数,它就不可能是其他任何方式。如果使用参数“my queue”调用它,那么在调用函数时将对该参数(符号)进行求值,并将求值结果(cons单元格)传递给函数。没有办法修改cons单元格来表示另一个cons单元格应该是“前置的”——cons单元格不跟踪指向它们的东西
有(至少)三种可能的解决方案:
- 编写代码,以便queuer返回新队列,而不是期望参数被修改(或“变异”)
- 将队列包装在可变的间接层中。例如,您可以在车内排队,或者在cons单元的cdr中排队。然后,您就可以在队列函数中进行变异(carresult)或(cdr result),例如使用push
- 将队列器转换为宏而不是函数。然后,您可以编写代码来修改它的参数,无论您在哪里使用queuer宏,这些参数都将被“插入”到您的代码中
(queuer-mutating data my-queue)
相反,您可以编写如下内容:
(setf my-queue (queuer-not-mutating data my-queue))
当您使用
(推送(pop数据)结果)
初始化数据
变量时,它会将项目从数据
移动到结果
,而不是复制:
CL-USER> (setq data '(("1" "text1") ("2" "text2") ("3" "text3")))
(("1" "text1") ("2" "text2") ("3" "text3"))
CL-USER> (setq result nil)
NIL
CL-USER> (push (pop data) result)
;Compiler warnings :
; In an anonymous lambda form: Undeclared free variable DATA (3 references)
(("1" "text1"))
CL-USER> (print data)
(("2" "text2") ("3" "text3"))
(("2" "text2") ("3" "text3"))
CL-USER> (print result)
(("1" "text1"))
(("1" "text1"))
您可能希望使用的是函数:
CL-USER> (setq copy2 (copy-list data))
(("2" "text2") ("3" "text3"))
不能使用只接受变量值的函数修改变量的内容 以下面的简单示例为例:
(defun futile-push (thing list)
(push thing list))
(let ((foo (list 1)))
(futile-push 2 foo))
会发生什么
将根据它所指向的列表进行计算Foo
计算结果为22
- 这两个参数被传递给函数
现在绑定到2Thing
现在绑定到列表列表
(1)
foo
函数外部
foo
|
v
---------
list -> | 1 |NIL|
---------
修改变量Push
,使其现在绑定到 列表list
(21)
foo
外部<代码>Foo仍然指向
和以前一样
foo
|
v
--------- ---------
list -> | 2 | ----> | 1 |NIL|
--------- ---------
返回Futile push
表单的返回值,该值会发生 作为push
列表的新值
- 该返回值从未被使用或绑定,因此它将消失
foo | v --------- | 1 |NIL| ---------
(let ((foo (list 1)))
(setf foo (not-so-futile-push 2 foo)))
如果你需要在多个地方这样做,这可能是值得的
为扩展到setf
窗体的对象编写宏。注意
正是出于这些原因,
push本身就是一个宏。如果用Lisp编写函数,最好考虑“功能”。函数接受值并返回值。一个典型的规则是避免副作用。因此,函数应该返回结果值,而不是“修改”变量值
而不是:
(defparameter *result* '())
(defun foo (a)
(push a *result*))
使用:
还请注意,aggregate
不需要progn
稍微高级(不要这样做):
如果您有一个全局列表:
(defparameter *foo* '())
正如我们所看到的,你不能像这样推它:
(defun foo (l)
(push 1 l))
如果调用foo
,变量*foo*
将保持不变。原因:Lisp不传递变量引用,它传递变量的值
但我们如何传递推荐信呢?好吧,传递一个引用:cons单元将执行此操作(或结构、向量、CLOS对象等):
现在,如果我们看一下*foo*
,它就变了。但我们并没有真正改变这个变量。我们已经更改了列表的第一个条目
CL-USER 42 > *foo*
((1 1))
但是,不要这样做。以功能性风格编写程序
(defun foo (l)
(push 1 l))
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)
CL-USER 42 > *foo*
((1 1))