Defining a “minimum” function to return the minimum of a list using another function that returns the smaller of two numbers

这一生的挚爱 提交于 2021-01-29 03:20:35

问题


(defun *smaller* (x y)
  ( if (> x y) y
        x))
(defun *minimum* (lst)
  (do ((numbers lst (cdr numbers))
       (result (car numbers) (*smaller* result (car numbers))))
      ((null numbers) result)))

LISP says that variable "numbers" in "minimum" function is an unbound one although I think I've bound it to "lst". What am I missing?


回答1:


Do binds in parallel. The expression for the initial value of result is (cdr numbers), and numbers is unbound there. Do* would work here.




回答2:


Another approach to this problem is to try and think about it inductively.

  • an empty list has no minimum;
  • if the list is not empty, its first element is a candidate minimum:
    • if the rest of the list is empty it's the answer
    • else the candidate minumum is the smaller of the current candidate and the first element of the list, and carry on through the rest of the list.

This translates very naturally into Lisp:

(defun smaller (x y)
  (if (< x y) x y))

(defun running-minimum (candidate tail)
  (if (null tail)
      candidate
    (running-minimum (smaller candidate (first tail)) (rest tail))))

(defun minimum (list)
  (when (null list)
    (error "?"))                        ;ed is the standard text editor
  (running-minimum (first list) (rest list)))

Notes

For good, if now mostly historical, reasons, CL makes no guarantees that code like this will be turned into an iterative process. Many implementations do so, but a conforming implementation need not, and even implementation which do so may not always do so, for instance in interpreted code or code compiled for ease of debugging. So a minimum function like the above is not really very idiomatic CL, and is certainly not completely safe if the code is meant to be portable.

However that is not my goal in this answer: one of the important things that you should get from learning lisp, in my opinion, is the ability to think inductively about solving problems. In other words to think about a class of problems which can be solved by starting from a simple base case and then steps which get from some general case closer to the base case, and then expressing this in code. This is a very different approach to programming than the approach which is natural in more imperative languages, and once you understand it it's a very powerful way of thinking.

As someone whose first serious language was FORTRAN (in the days when that was the correct way of writing its name even!) I think that this inductive approach is very important, and it's that which I wanted to get across here.




回答3:


(defun smaller (x y)
  "returns the smaller number"
  (if (> x y)
      y
    x))

(defun minimum (list)
  "returns the smallest number of a list"
  (do* ((numbers list (rest numbers))
        (result
         (first numbers)
         (if numbers
             (smaller result (first numbers))
           result)))
       ((null numbers) result)))



回答4:


First of all, as pointed above, you should use do* instead of do, in order to bind the variables sequentially. Second, you are testing (null numbers) to end the loop. However, when only one element is left to process, numbers is not nil but then it becomes (cdr numbers) which is nil. This nil value is what is being passed to *smaller* as second argument, and that's why you get the error message. To avoid this, you should test for (null (cdr numbers)) instead:

(defun *smaller* (x y)
  ( if (> x y) y
        x))
(defun *minimum* (lst)
  (do* ((numbers lst (cdr numbers))
       (result (car numbers) (*smaller* result (car numbers))))
      ((null (cdr numbers)) result)))

This exits the loop when there is only one item left in the list.



来源:https://stackoverflow.com/questions/59920951/defining-a-minimum-function-to-return-the-minimum-of-a-list-using-another-func

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!