Lisp:如何打印出递归函数来打印列表和子列表中没有引号的每个项目并返回项目数?

时间:2014-03-12 11:54:35

标签: recursion lisp common-lisp

我希望我的函数打印列表和子列表中没有引号的每个项目,并返回项目数。列表的输出也需要按顺序排列,但我的功能是反向打印。我不知道为什么,有什么原因吗?有关如何递归计算项目数量并返回该数字的任何建议?另外,为什么最后打印的项目应该是9.99而不是100.999?

编辑:感谢您的帮助到目前为止。最后一个问题:有没有办法让像DAY这样的输出处于小写(白天),或者是那些无法完成的事情?

我的功能:

(defun all-print (inlist)
    (cond
        ((not (listp inlist)) 
            (format t "Error, arg must be a list, returning nil")
            ())          
        ((null inlist) 0) 
        ((listp (car inlist)) 
            (ffn (append (car inlist)(cdr inlist))))
        (t
            (format t "~a " (car inlist) (ffn (cdr inlist))))))  

我的输出示例:

CL-USER 1 >  (all-print (list 5 "night" 3 (list 9 -10) (quote day) -5.9 (* 100.999)))
100.999 -5.9 DAY -10 9 3 night 5 
NIL

它输出的例子是什么:

5 night 3 9 -10 day -5.9 9.99 ;print
8 ;returns

2 个答案:

答案 0 :(得分:2)

看起来all-print应该被称为ffn,因为它看起来应该是递归调用。在本回答的其余部分中,我将使用ffn,因为它更短。

为什么输出反向

目前,您的最终cond子句在进行任何打印之前进行递归调用,因为您的递归调用是format的参数:

(format t "~a " (car inlist) (ffn (cdr inlist)))
;               ------------ -----------------
;                     3rd          4th

format的所有参数,包括本例中的第4个参数,都会在调用format之前进行评估。这里的第四个参数将打印列表的 rest ,然后format将最终打印列表的第一个元素。您的上一个cond子句应该进行打印,然后进行递归调用:

(cond 
  …
  (t
    (format t "~a " (car inlist))
    (ffn (cdr inlist))))

为什么你得到100.999而不是9.99

你的输出中得到100.999而不是9.99(或接近它的东西),因为(* 100.999)的值只是100.999的值。我猜你想要(* 10 0.999)(注意100.99之间的空格)。然而,由于浮点运算,它仍然不会很9.99,但它会很接近。

如何获取打印元素的数量

uselpa's answer在这里提供了一个很好的解决方案。如果您应该返回打印的元素数,那么此函数的每个返回值都应该是一个数字。你有四个案例,

  • 不是列表 - 返回nil不是一个好主意。如果这不能返回一个数字(例如,0),则发出实际错误信号(例如,使用(error "~A is not a list" inlist)
  • inlist为空 - 返回0(您已经这样做)
  • (car inlist)是一个列表 - 在这里您对ffn进行递归调用。由于合同说它将返还一个计数,你没事。这是在第一种情况(不是列表)中如此重要的原因之一,即您不返回非数字;合同取决于每个返回一个号码的电话。
  • 在最后一种情况下,您打印一个项目,然后对ffn进行递归调用。该递归调用返回打印的剩余元素的数量,并且由于您只打印了一个元素,因此需要向其中添加一个元素。因此,最终的cond子句实际上应该类似于以下内容。 (添加一个东西是如此常见,以至于Common Lisp具有1+功能。)

    (cond 
      …
      (t
        (format t "~a " (car inlist))
        (1+ (ffn (cdr inlist)))))     ; equivalent to (+ 1 (ffn (cdr inlist)))
    

更有效的解决方案

我们已经解决了您原始代码的问题,但我们也可以询问是否有更好的方法来解决问题。

不要追加

请注意,当您输入((a b c) d e f)时,您可以创建列表(a b c d e f)并对其进行递归。但是,您可以在(a b c)(d e f)上等效递归,并将结果一起添加。这样可以避免使用append创建新列表。

不检查参数类型

您正在检查输入是否为列表,但实际上并不需要这么做。如果输入不是列表,则使用列表处理功能将发出类似的错误信号。

新版本

这有点类似于uselpa's answer,但我对如何处理某些事情做了一些不同的选择。我使用本地函数process-element来处理每个输入列表中的元素。如果元素是一个列表,那么我们递归地将它传递给print-all,并返回递归调用的结果。否则我们返回一个并打印该值。 (我使用(prog1 1 …)来强调我们正在返回一个,而打印只是副作用。print-all的主要部分现在是典型的递归。

(defun print-all (list)
  (flet ((process-element (x)
           (if (listp x)
               (print-all x)
               (prog1 1
                 (format t "~A " x)))))
    (if (endp list)
        0
        (+ (process-element (first list))
           (print-all (rest list))))))

当然,既然我们已经取出了辅助函数,那么迭代会更加清晰,我们发现它实际上是reduce的情况。你甚至可以选择取消本地函数,只使用lambda函数:

(defun print-all (list)
  (reduce '+ list
          :key (lambda (x)
                 (if (listp x)
                     (print-all x)
                     (prog1 1
                       (format t "~A " x))))))

答案 1 :(得分:0)

以下是关于如何编写此功能的建议:

(defun all-print (lst)
  (if (null lst)
    0                    ; empty list => length is 0
    (let ((c (car lst))) ; bind first element to c
      (if (listp c)      ; if it's a list
        (+ (all-print c) (all-print (cdr lst))) ; recurse down + process the rest of the list
        (progn           ; else
          (format t "~a " c)              ; not a list -> print item, then
          (1+ (all-print (cdr lst)))))))) ; add 1 and process the rest of the list

然后

? (all-print (list 5 "night" 3 (list 9 -10) (quote day) -5.9 (* 100.999)))
5 night 3 9 -10 DAY -5.9 100.999 
8