常见的Lisp Loop宏变量绑定

时间:2018-07-17 10:47:22

标签: loops common-lisp

我在理解with关键字的工作方式时遇到了问题。特别是,我认为它与let语句相同,但是没有遵循。

例如,这两个代码“应该”打印相同的值,但是第一个给出(nil nil),而后者可以正常工作。

(loop for c in clauses
    with p = (car c)
    collect p)

(loop for c in clauses
    collect (car c))

2 个答案:

答案 0 :(得分:7)

with用于创建局部辅助变量。它们在循环开始前初始化,因此与编写此代码相同:

(let ((p (car c))
  (loop for c in clauses
    collect p))

除了c的同时nil(car c)的形式存在的事实。我认为是因为loop一开始就一次性创建了所有变量,就像我在此处创建p一样。

您正在寻找for

(loop 
  for c in clauses
  for p = (car c)
  collect p)

为什么不进行销毁呢?:

(loop 
  for (p) in clauses
  collect p)

答案 1 :(得分:5)

有助于更好地理解LOOP的一件事是LOOP具有三个不同的子句部分

(loop

  ; first a single optional NAME clause

  ; then zero or more variable clauses with WITH, INITIAL, FINALLY and/or FOR

  ; then zero or more main clauses with DO, RETURN, COLLECT, APPEND, SUM, NCONC, ...
  )

必须保持这些子句部分的顺序。

有两种引入变量的方法:FORWITHFOR会在每次迭代中更新变量,而WITH只会执行一次。您可以在正确的部分中以任何顺序编写这些子句,但是通常WITH绑定及其值将在FOR变量具有正确值之前创建-尽管并非总是如此。

LispWorks警告您的特定代码:

CL-USER 6 > (loop for c in '((1) (2) (3))
                  with p = (car c)
                  collect p)

Warning: Local Variable Initialization clause
  (the binding of P) follows iteration forms but will be evaluated before them.

(NIL NIL NIL)

经常

(loop for c in clauses
    with p = (car c)
    collect p)

将通过以下方式实现:

(...
   (let ((c nil) ...)
     (let ((p (car c)))   ; your (CAR ...)  form

        ; iteration code ...

     )))

在这种情况下,您有一些“运气”,因为(car nil)可以正常工作,只有结果不是您所期望的-默默无闻。

但这会产生错误:

(loop for c in '((1) (2) (3))
      with p = (1+ c)    ; note the 1+ instead of CAR
      collect p)

此处(1+ nil)不起作用,并且会出错,因为函数1+仅接受数字作为参数。您不会看到意外的结果,但是会看到错误。

样式规则

  • 请勿混用FORWITH子句。
  • WITH子句之前写入FOR子句。
  • 不依赖于实现特定的行为和效果。