【问题标题】:Lisp: How to print out the recursive function to print each item in the list and sublist without quotes and return the number of items?Lisp:如何打印出递归函数以打印不带引号的列表和子列表中的每个项目并返回项目数?
【发布时间】:2014-03-12 11:54:35
【问题描述】:

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

【问题讨论】:

    标签: recursion lisp common-lisp


    【解决方案1】:

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

    为什么输出是反向的

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

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

    format 的所有参数,包括本例中的第 4 个参数,都在调用 format 之前进行评估。这里的第 4 个参数将打印列表的 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)(注意10 和0.99 之间的空格)。不过,由于浮点运算,这仍然不会完全是 9.99,但它会很接近。

    如何获取打印的元素数量

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

    • 不是一个列表——返回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))))))
    

    【讨论】:

      【解决方案2】:

      以下是我对如何编写此函数的建议:

      (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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-10-28
        • 1970-01-01
        • 2019-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多