Return 来自循环内的格式

Return from format inside loop

我是 Lisp 新手,在理解 loopformat 组合时的工作原理时遇到了一些困难。

这符合我的预期:

(loop for i upto 2 do (format t "~a" "Badger")) ==> BadgerBadgerBadger NIL

这不是:

(loop for i upto 2 do (format nil "~a" "Badger")) ==> NIL

为什么第二个循环不是 return BadgerBadgerBadger?我必须编写什么代码才能赋予此 return 值?

这取决于format函数的第一个参数,叫做destination(见manual):

If destination is a string, a stream, or t, then the result is nil. Otherwise, the result is a string containing the ‘output’

在第一种情况下,format 写入标准输出(您会看到三个 "Budger")和 returns NIL。但是您看不到 那个 值,而是 return 由 loop 编辑的值,即 NIL。事实上,LOOP 中没有子句(如 RETURN)到 return 与 NIL 不同的东西。

第二种情况,format return是字符串,但是循环的值又是NIL,就是整个表格的结果。

如果你想return格式的结果,你可以,例如,写:

(with-output-to-string (s)
   (loop for i upto 2 do (format s "~a" "Badger")))

这样,format函数"write"到字符串流s,由with-output-to-string编辑return。

return输入值和打印值之间存在重要区别。有时这在 REPL 中会造成混淆,因为 return 值默认打印:

CL-USER> (+ 1 1)    ; form *returns* 2
2                   ; and return value (2) is printed
CL-USER> (let () 
           (+ 1 1)  ; (+ 1 1) still returns 2, but
           nil)     ; the return value of the (let ...) is NIL
NIL                 ; so NIL is printed

现在,格式可以做一些不同的事情,这取决于它的第一个参数。如果它的第一个参数是 t 或一个流,那么它将输出写入流并且 returns nil:

CL-USER> (format t "hello")
hello                         ; printed output
NIL                           ; return value from format

CL-USER> (let ()             
           (format t "hello") ; will print "hello"
           42)                ; but the whole form returns 42
hello                         ; printed output
42                            ; printed return value

当使用 nil 作为第一个参数调用 format 时,它 returns 作为字符串生成的输出:

CL-USER> (format nil "hello")
"hello"                           ; return value, not printed output

CL-USER> (let ()
           (format nil "hello")   ; returns "hello"
           42)                    ; but the whole form returns 42
42                                ; printed return value

现在,你可以收集循环的结果,听起来你想用格式生成一个字符串,然后收集那些字符串:

CL-USER> (loop for i upto 2 collect i)
(0 1 2)

CL-USER> (loop for i upto 2 collect (* 8 i))
(0 8 16)

CL-USER> (loop for i upto 2 collect (format nil "string number ~a" i))
("string number 0" "string number 1" "string number 2")