公共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行) (连接“字符串(第

我有以下常见的lisp函数:
(聚合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
    将根据它所指向的列表进行计算
  • 2
    计算结果为2
  • 这两个参数被传递给函数
在函数调用内部:

  • Thing
    现在绑定到2
  • 列表
    现在绑定到列表
    (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))