简体   繁体   English

Lisp循环宏、pop宏和memory

[英]Lisp loop macro, pop macro and memory

I'm learning lisp and and implemented a nested array parses, so that:我正在学习 lisp 并实现了嵌套数组解析,因此:

"[1,[2,[3,[4,[5,6,7]]]],8,9]" -> '(1 (2 (3 (4 (5 6 7)))) 8 9)

Online Code snippet here在线代码片段在这里

This was my first implementation of parse-line这是我第一次实现parse-line

(defun parse-line (string)
     (loop 
          :for i = 0 :then (1+ j)
          :for stack = (list nil)
          :as j = (position-if (lambda (c) (member c '(#\[ #\] #\,))) string :start i)
          :as n = (parse-integer string :start i :end j :junk-allowed t)         
          :when n :do (push n (car stack))
          :while j
          :do (cond
            ((eql #\[ (aref string j)) (push nil stack))
            ((eql #\] (aref string j)) (push (nreverse (pop stack)) (car stack))))
          :finally (return (car (pop stack))))
        ))

But it crashes on the stack pop.但它在堆栈弹出时崩溃。

;test-case        
(print (equal '(1 (2 (3 (4 (5 6 7)))) 8 9) (parse-line "[1,[2,[3,[4,[5,6,7]]]],8,9]")))

The value NIL is not of type CONS值 NIL 不是 CONS 类型

I then brute-forced my way into a solution declaring stack with let outside the loop macro然后let强行进入一个解决方案,在loop宏之外声明stack

(defun parse-line (string)
    (let ((stack (list nil)))
     (loop 
          :for i = 0 :then (1+ j)
          :as j = (position-if (lambda (c) (member c '(#\[ #\] #\,))) string :start i)
          :as n = (parse-integer string :start i :end j :junk-allowed t)         
          :when n :do (push n (car stack))
          :while j
          :do (cond
            ((eql #\[ (aref string j)) (push nil stack))
            ((eql #\] (aref string j)) (push (nreverse (pop stack)) (car stack))))
          :finally (return (car (pop stack))))
        ))

But I can't see why the second implementation works.但我不明白为什么第二个实现有效。

I also can't see why I need to initialize stack with (list nil) .我也不明白为什么我需要用(list nil)初始化stack I thought that initializing stack to nil and removing the final car should be equivalent but it crashes with the same error at the same place, ie:我认为将stack初始化为nil并删除最终的car应该是等效的,但它在同一位置因相同的错误而崩溃,即:

(defun parse-line (string)
        (let (stack)
         (loop 
              :for i = 0 :then (1+ j)
              :as j = (position-if (lambda (c) (member c '(#\[ #\] #\,))) string :start i)
              :as n = (parse-integer string :start i :end j :junk-allowed t)         
              :when n :do (push n (car stack))
              :while j
              :do (cond
                ((eql #\[ (aref string j)) (push nil stack))
                ((eql #\] (aref string j)) (push (nreverse (pop stack)) (car stack))))
              :finally (return (pop stack)))
            ))

Look at these two lines in your first implementation:在您的第一个实现中查看这两行:

:for i = 0 :then (1+ j)
:for sack = (list nil)

There is a typo ( sack -> stack ), but even then it doesn't work, because with each new value of i , you will create a brand new stack , losing all previous values.有一个错字( sack -> stack ),但即使那样它也不起作用,因为对于i的每个新值,您将创建一个全新的stack ,丢失所有以前的值。 Add some debug print to see, what's going on:添加一些调试打印以查看发生了什么:

i:0 j:0 n:NIL stack:(NIL NIL)
i:1 j:2 n:1 stack:((1))
i:3 j:3 n:NIL stack:(NIL NIL)
i:4 j:5 n:2 stack:((2))
i:6 j:6 n:NIL stack:(NIL NIL)
i:7 j:8 n:3 stack:((3))
...

If you don't want to use let to initialize stack , you can use keyword :with :如果你不想使用let来初始化stack ,你可以使用关键字:with

(defun parse-line (string)
  (loop 
   :with stack = (list nil)
   :for i = 0 :then (1+ j)
   :as j = (position-if (lambda (c) (member c '(#\[ #\] #\,))) string :start i)
   :as n = (parse-integer string :start i :end j :junk-allowed t)         
   :when n :do (push n (car stack))
   :while j
   :do  (cond
         ((eql #\[ (aref string j)) (push nil stack))
         ((eql #\] (aref string j)) (push (nreverse (pop stack)) (car stack))))
   :finally (return (car (pop stack)))))

You can use a similar debug print to see, why your code crashes with stack initialized to nil .您可以使用类似的调试打印来查看为什么您的代码在stack初始化为nil时崩溃。 This error can be reproduced with:此错误可以通过以下方式重现:

> (let ((stack '((9 8 (2 (3 (4 (5 6 7)))) 1))))
    (push (nreverse (pop stack)) (car stack)))

Error: NIL (of type NULL) is not of type CONS.

You can find some explanation for this behaviour here: Why I can't (push 3 '()) in Common Lisp's REPL?您可以在此处找到有关此行为的一些解释: Why I can't (push 3 '()) in Common Lisp's REPL?

I would recommend using a Lisp compiler and using its warnings.我建议使用 Lisp 编译器并使用它的警告。 SBCL gives this warning on your first form: SBCL 在您的第一份表格中给出此警告:

; in: DEFUN PARSE-LINE
;     (PUSH N (CAR STACK))
; 
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::STACK
; 
; compilation unit finished
;   Undefined variable:
;     STACK
;   caught 1 WARNING condition

That would lead you to investigate why that is: why is STACK undefined?这将引导您调查原因:为什么 STACK 未定义?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM