仅使用构造函数中的列表在Lisp中查找列表中的最小数字?

时间:2017-12-16 23:20:46

标签: list recursion lisp

我的教授在期中给了我们这个难题,除非我没有正确地看待它,否则这个问题看起来很简单。

  
      
  1. 编写一个查找列表最小值的lisp函数f。假设该列表仅包含数字。例如(f'(3 2 5 4 9   5))返回2.
  2.   

这是我到目前为止所拥有的:

(defun f (L)
 (cond ((null (cdr L))(car L))    ; <-- I think my break case is wrong, too.
       ((< (car L) (car (cdr L))) (rotatef((car L) (car(cdr L)))))
 (f(cdr L))
 )
)

编译器告诉我,我对rotatef的使用很糟糕。

我的逻辑是不断地用car(cdr L)交换最小元素,并始终将car(L)作为最小元素。然后以递归方式调用cdr,直到只有我认为这样做的唯一方式。奇怪的是,他在我们的笔记中从未提及rotatef,所以我认为我做得不好。

哦,很好的Lisp诸神,你能帮帮我吗?你是我唯一的希望。

5 个答案:

答案 0 :(得分:2)

  • 你应该计算一个最小值,所以让我们调用函数minimum
  • L作为变量名称可以替换为list
  • 改进缩进和括号

然后它看起来像这样:

(defun minimum (list)
 (cond ((null (cdr list))
        (car list))    ; <-- I think my break case is wrong, too.
       ((< (car list) (car (cdr list)))
        (rotatef ((car list)
                  (car (cdr list)))))
       (minimum
        (cdr list))))

现在你可以摆脱CARCDR

(defun minimum (list)
 (cond ((null (rest list))
        (first list))    ; <-- I think my break case is wrong, too.
       ((< (first list) (second list))
        (rotatef ((first list)
                  (second list))))
       (minimum
        (rest list))))

最后一个cond子句中的最小值没有意义,因为它需要一个带有布尔值的列表:

(defun minimum (list)
 (cond ((null (rest list))
        (first list))    ; <-- I think my break case is wrong, too.
       ((< (first list) (second list))
        (rotatef ((first list)
                  (second list))))
       (t (minimum (rest list)))))

ROTATEF需要多少参数? ((foo) (bar))在Lisp中没有用,因为你不能在一堆Lisp表单周围加上括号。

(defun minimum (list)
 (cond ((null (rest list))
        (first list))    ; <-- I think my break case is wrong, too.
       ((< (first list) (second list))
        (rotatef (first list)
                 (second list)))
       (t
        (minimum (rest list)))))

什么是空列表?

(defun minimum (list)
 (cond ((null list)
        nil)
       ((null (rest list))
        (first list))
       ((< (first list) (second list))
        (rotatef (first list)
                 (second list)))
       (t
        (minimum (rest list)))))

ROTATEF毫无意义,因为它没有返回最小值。

(defun minimum (list)
 (cond ((null list)
        nil)
       ((null (rest list))
        (first list))
       ((< (first list) (second list))
        (minimum (cons (first list)
                       (rest (rest list)))))
       (t
        (minimum (rest list)))))

最后添加一个简短的文档字符串。

(defun minimum (list)
 "recursive function to return the minimum value of a list of numbers"
 (cond ((null list)
        nil)
       ((null (rest list))
        (first list))
       ((< (first list) (second list))
        (minimum (cons (first list)
                       (rest (rest list)))))
       (t
        (minimum (rest list)))))

说明:

(defun minimum (list)

 "recursive function to return the minimum value of a list of numbers"

 (cond

       ((null list)                      ; list is empty
        nil)

       ((null (rest list))               ; only one element
        (first list))

       ((< (first list) (second list))   ; if first element is smaller than second
        (minimum (cons (first list)      ; call minimum without second element 
                       (rest (rest list)))))

       (t                                ; second is equal or smaller
        (minimum (rest list)))))         ; call minimum without first element

现在我们也要测试它:

CL-USER 24 > (let ((lists '(()
                            (1)
                            (1 2)
                            (2 1)
                            (3 2 1 0)
                            (1 2 3)
                            (3 4 2 1 1)
                            (1 1 12 -1 4 2))))
               (mapcar #'minimum lists))

(NIL 1 1 1 0 1 1 -1)

答案 1 :(得分:0)

Parens are meaningful in lisp:这就是问题所在 与您的rotatef

您还需要使用funcall: 而不是P(f ...),这是lisp-1 vs lisp-2问题。

答案 2 :(得分:0)

Go back to the basics.

What's the shortest list your function can receive? The empty list. What should we return for that? It's not clear. Let's just return nil.

What's the next shortest list your function can receive? A list containing a single number. What should return for that? We should return that single number.

Otherwise, we have a list with at least two numbers. There's (car L) (the first number in the list), and there's (cdr L) (all the rest of the numbers in the list). If (car L) is smaller than the smallest number in (cdr L), then we should return (car L). Otherwise, we should return that smallest number from (cdr L).

Now, how can we get the smallest number from (cdr L)? Well, we're already writing the function that does that! We can call (f (cdr L)) to get the smallest number in (cdr L).

(defun f (L)
    (cond
        ; empty list
        ((null (car L)) nil)

        ; list with just one element
        ((null (cdr L)) (car L))

        ; 2 or more elements
        (T (let ((head (car L))
                 (tailMin (f (cdr L))))
                (if (< head tailMin) head tailMin)))))

答案 3 :(得分:0)

这真是对Rainer Joswig的漂亮答案的延伸评论。

他的minimum函数的作用是将列表的第一个元素视为最小的运行最佳猜测,重复构建一个新的,更短的列表,将这个最佳猜测作为其第一个元素。这真的很聪明,因为找到最小值的整个算法确实是这样的:猜测第一个数字,将它与第二个数字进行比较并适当递归。

解决此类问题的另一种方法是将此运行最佳猜测作为计算最小值的函数的独特参数。当像这样使用时,这个替代参数有时被称为“累加器”&#39;因为在其他相关模式中,它用于累积结果。这种模式只有在你愿意定义一个辅助函数来完成这项工作时才能真正起作用,因为你通常不希望顶级函数有这个额外的参数。

所以,这里的Rainer函数改写如下:

(defun minimum/tfb (list)
   "function to return the minimum value of a list of numbers, via a recursive helper"
   (when (null list)
     ;; I claim that the empty list is an error, so let's check this
     ;; and say so
     (error "empty lists don't have minima"))
   (labels ((minimum-loop (tail min-so-far)
              ;; TAIL is everything else we need to look at,
              ;; MIN-SO-FAR is the running minimum
              (cond ((null tail)
                     ;; we are done: the running minimum is the minimum
                     min-so-far)
                    ((< min-so-far (first tail))
                     ;; the running minimum is smaller than the first
                     ;; element of TAIL so it is still our best bet:
                     ;; recurse on the rest of TAIL
                     (minimum-loop (rest tail) min-so-far))
                    (t
                     ;; the first element of TAIL is less than or
                     ;; equal to min-so-far, so it is now our best bet
                     (minimum-loop (rest tail) (first tail))))))
     ;; Now start the process (remember we know LIST has at least one
     ;; element, which we use as out best guess).
     (minimum-loop (rest list) (first list))))

很容易看出这与Rainer的功能相同(除非它给出的列表是空的,这是一个意见问题) ,但它使用这个本地minimum-loop函数来完成工作,并有这个额外的参数。

通过观察你可以在通话点计算额外的参数,可以用一种稍微更无偿的方式重写它:

(defun minimum/tfb/gratuitous (list)
   "function to return the minimum value of a list of numbers, via a recursive helper"
   (when (null list)
     ;; I claim that the empty list is an error, so let's check this
     ;; and say so
     (error "empty lists don't have minima"))
   (labels ((minimum-loop (tail min-so-far)
              ;; TAIL is everything else we need to look at,
              ;; MIN-SO-FAR is the running minimum
              (if (null tail)
                  ;; we are done: the running minimum is the minimum
                  min-so-far
                ;; We have more to do: recurse, deciding which element
                ;; to use as the running minimum
                (minimum-loop (rest tail)
                              (if (< min-so-far (first tail))
                                  min-so-far (first tail))))))
     ;; Now start the process (remember we know LIST has at least one
     ;; element, which we use as out best guess).
     (minimum-loop (rest list) (first list))))

我不认为这在风格上更好,而且可能更糟。这可能是我写的东西。 (Python程序员讨厌我的代码。)

最后请注意,Rainer的功能和我对它的修改都是尾递归的。在像Scheme这样的语言中,这些函数对应于显式迭代过程。实际上,在Scheme中,迭代构造在尾部调用方面是定义的。 Scheme对于这种辅助函数模式也有一个很好的语法。

这是我的第二个函数到Scheme-family语言(Racket)的简单音译:

(define (minimum lyst)
  (when (null? lyst)
    (error "empty lists do not have minima"))
  (define (minimum-loop tail min-so-far)
    (if (null? tail)
        min-so-far
        (minimum-loop (rest tail)
                      (if (< min-so-far (first tail))
                          min-so-far
                          (first tail)))))
  (minimum-loop (rest lyst) (first lyst)))

(请注意list令人讨厌的拼写为lyst,因为Scheme是Lisp-1,并注意内部define的工作方式类似于labels。)

以下是使用名为 - let的转换,其目的是支持此类事情:

(define (minimum/loop lyst)
  (when (null? lyst)
    (error "empty lists do not have minima"))
  (let minimum-loop ([tail (rest lyst)]
                     [min-so-far (first lyst)])
    (if (null? tail)
        min-so-far
        (minimum-loop (rest tail)
                      (if (< min-so-far (first tail))
                          min-so-far
                          (first tail))))))

答案 4 :(得分:-1)

(defun F(L)   (申请'min L) )