尝试在Lisp中递归打印三角形

时间:2019-10-29 16:47:28

标签: arrays recursion common-lisp clisp cons

尝试以Lisp递归方式打印三角形。我溢出,但是我不知道从哪里来。请注意,我是Lisp编程的新手。

(defun triangle (n)
    (if (not (oddp n))(progn 
        (print "This is not an odd integer")
        (return-from triangle n)))   
    (if (< n 1) '())
            (setf lst (cons (car(list n)) (triangle (- n 2))))
    (print lst))

enter image description here

(三角形7)

4 个答案:

答案 0 :(得分:2)

让我们回顾一下代码的某些部分。

(if (not (oddp n)) (progn (print ...) (return-from ...)))

if仅具有一个分支时,请考虑使用whenunless,尤其是 如果您在子表单中放置了progn。例如,您可以通过切换来摆脱prognwhen

(when (not (oddp n))
  (print ...)
  (return-from ...))

(when (not ...))表达式与(unless ...)相同:

(unless (oddp n)
  ...)

在这里,您将输出一条错误消息,然后从函数中返回。 Common Lisp有例外,这些例外是针对那些用例的。 您通常会致电(error "Not an odd integer: ~d" n), 但是在这里您可以依靠assert的默认行为,并且 将整个支票替换为:

(assert (oddp n))

如果尝试使用2,您将获得一条错误,并显示类似以下消息:

The assertion (ODDP N) failed with N = 2.

足够进行测试了。

然后,您具有以下表达式:

(cons (car (list n)) (triangle (- n 2)))

(list e1 e2 .. en)时,就像写了一样:

(cons e1 (cons e2 (... (cons en nil))))

在您的情况下,这意味着(list n)与以下内容相同:

(cons n nil)

但是自从您执行以下操作后,

(car (cons n nil))

实际上,您只是在分配一个单元并丢弃它只是为了访问n。 整个表达式可以替换为n

第三,您还对setf使用lst,其中lst是未定义的变量。 在大多数实现中(但实际上未指定此行为),这将设置全局绑定 对于lst而言,从函数内部设置全局变量是一种不好的做法。 您可以改用let

(let ((lst (cons n (triangle (- n 2)))))
  (print lst))

但是该变量仅使用一次,您也可以内联它:

(print (cons n (triangle (- n 2))))

最后,您拥有:

(defun triangle (n)
  (assert (oddp n))
  (if (< n 1)
      ()
      (print (cons n (triangle (- n 2))))))

这是样式问题,但是请记住,通常可以替换在其分支之一中返回if的{​​{1}} 由nilwhen(此处为unless)创建。有些人不喜欢这样,更喜欢不依赖unlessnil的{​​{1}}返回值。 无论如何,让我们对其进行测试:

when

给出:

unless

请注意列表如何向后移动。 解决问题的一种方法是将(triangle 7) 替换为(1) (3 1) (5 3 1) (7 5 3 1) ,但这是行不通的。你知道为什么吗?

相反,让我们向后构建列表,该列表需要从1开始向上计数,直到达到(print ...)

(print (reverse ...))

由于第二个参数是可选的,因此我们可以像以前一样调用它:

n

但是现在,输出为:

(defun triangle (n &optional (c 1))
  (assert (oddp n))
  (when (<= c n)
    (print
      (cons c (triangle n (+ c 2))))))

答案 1 :(得分:1)

括号错误!根据您的缩进,我相信您需要以下内容:

(if (< n 1) '())
    (setf ...

成为if-then-else,其中setf在else分支中。为此,它应该看起来像:

(if (< n 1) '()
    (setf ...

在当前设置中,始终评估setf

答案 2 :(得分:0)

一个有用的策略是将三角形的构建与显示分开。 这样,您可以更改打印格式,而无需更改生成三角形的代码。

让我们选择将三角形表示为列表列表 这样create-triangle 7 => ((1) (1 3) (1 3 5) (1 3 5 7))。 以下实现生成所需的三角形:

(defun create-triangle (n)
  (labels ((aux (x acc)
             (if (> x n)
                 acc
                 (let* ((hd (car acc))
                        (new (append hd (list x))))
                   (aux (+ x 2) (cons new acc))))))
    (when (and (plusp n) (oddp n))
      (reverse (aux 1 nil)))))

注意事项:Lisp是一种面向表达式的语言 因此,通常,不需要返回类似的语句。

辅助功能aux接受整数x并 列表acc。如果x大于n,则返回acc 否则,我们创建2个局部变量:hd保存第一个列表 在我们的累加器中找到:这将是最新行 计算的。 new由当前数字x附加而成 到hd。例如,如果acc = ((1 3 5) (1 3) (1))x = 7,然后hd = (1 3 5)new =(1 3 5 7). Having made this computation, we call aux with the new x bound to x + 2 which is 7 in out example, and acc bound to ( (1 3 5 7)(1 3 5)(1 3)(1)). This way we build the triangle in reverse. The function创建三角形, first checks that n is both positive and odd, and when that is satisfied, it returns the reverse of the triangle built by aux. Thus we get((1)(1 3)(1 3 5 )(1 3 5 7))in our example. If n`不是正数和奇数,它只返回空列表。 如Coredump所述,您将其更改为进行错误检查。

现在我们有了三角形,剩下的就是打印三角形。 我们使用以下功能来做到这一点:

(defun print-triangle (xss)
  (when xss
    (format t "~&~{~a ~}" (car xss))
    (print-triangle (cdr xss))))

print-triangle有趣的部分是format表达式。 t表示输出为stdout,通常为 屏幕,在控制字符串中; ~&的意思是:确保我们换了一行,并且 ~{~a ~}打印列表的内容,并在它们之间留一个空格, (car xss)是格式参数。

现在我们可以将两个功能放在一起

(defun triangle (n)
  (print-triangle (create-triangle n)))

快速测试:

CL-USER> (triangle 19)
1
1 3
1 3 5
1 3 5 7
1 3 5 7 9
1 3 5 7 9 11
1 3 5 7 9 11 13
1 3 5 7 9 11 13 15
1 3 5 7 9 11 13 15 17
1 3 5 7 9 11 13 15 17 19
NIL

答案 3 :(得分:0)

将问题分解成小块始终是一个好习惯,这种方法还使您的代码更具模块化,因此您可以更安全,灵活地更改部分代码。

首先,您可以定义一个小函数以将三角形的每一行收集为一个列表(并且由于您要求使用递归解,因此它也是递归定义的):

(defun upwards-range (x y &optional (by 2))
  "Returns a list containing numbers from X to Y
by BY steps."
   (if (>= x y)
      (list y)
      (cons x (upwards-range (+ x by) y by))))

(upwards-range 1 7) ; => (1 3 5 7)

第二个创建一个函数,该函数收集三角形的所有行。请注意,这里我隐含地期望参数N是一个奇数。在下一个函数show-triangle中,您可以断言并返回错误。

(DEFUN TRIANGLE (N)
  "COLLECTS TRIANGULAR LISTS."
  (IF (= -1 N)
      NIL
      (APPEND (TRIANGLE (- N 2))
          (LIST (UPWARDS-RANGE 1 N)))))

(TRIANGLE 7) ; => ((1) (1 3) (1 3 5) (1 3 5 7))

还有打印三角形的功能:

(defun print-triangle (N)
  "Makes sure that N is an odd number and prints the 
rows of the triangle if so."
  (assert (oddp N) (N) "~S is an even number. Please supply an odd onve" N)
  (let ((rows (triangle N)))
    (dolist (row rows)
      (print row))))

(print-triangle 7)

返回:

(1) 
(1 3) 
(1 3 5) 
(1 3 5 7)