【问题标题】:Defining a "minimum" function to return the minimum of a list using another function that returns the smaller of two numbers使用另一个返回两个数字中较小者的函数定义“最小”函数以返回列表的最小值
【发布时间】:2020-05-12 05:27:38
【问题描述】:
(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 说“最小”函数中的变量“数字”是未绑定的,尽管我认为我已将其绑定到“lst”。我错过了什么?

【问题讨论】:

  • 我知道,这个可能是为了教育,但是使用reduce不是更好吗?只是(reduce #'*smaller* lst) ?
  • 另外,在函数名中使用*earmuffs* 是反习惯用法,因为这种风格通常用于特殊变量。
  • 感谢您提供的信息,我对编码很陌生。是的,这是我的 uni 课程的作业,我不是 Comp sci 学生。

标签: lisp common-lisp unbound


【解决方案1】:

Do 并行绑定。 result 的初始值表达式为(cdr numbers)numbers 在那里未绑定。 Do* 可以在这里工作。

【讨论】:

  • Tysm 我不知道这个。尽管如此,当我使用“do*”而不是“do”时,lisp 说 NIL' 不是预期的“REAL”类型。你们能给我一些关于我在哪里犯错的建议,或者换一种方式来思考这个问题吗?
【解决方案2】:

解决这个问题的另一种方法是尝试归纳思考。

  • 空列表没有最小值;
  • 如果列表不为空,则其第一个元素是候选最小值:
    • 如果列表的其余部分为空,则为答案
    • 否则,候选最小值是当前候选和列表的第一个元素中较小的一个,并继续遍历列表的其余部分。

这很自然地转化为 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)))

注意事项

出于好的原因,如果现在主要是历史原因,CL 不保证这样的代码会变成一个迭代过程。许多实现都这样做,但一致的实现不需要,甚至这样做的实现也可能并不总是这样做,例如在解释代码或为便于调试而编译的代码中。因此,像上面这样的 minimum 函数并不是非常惯用的 CL,如果代码是可移植的,那么肯定不是完全安全的。

然而,这不是我在这个答案中的目标:在我看来,你应该从学习 lisp 中获得的重要东西之一是归纳思考解决问题的能力。换句话说,考虑一类可以通过从简单的基本情况开始,然后从一些更接近基本情况的一般情况中得到的步骤来解决的问题,然后在代码中表达出来。这是一种非常不同的编程方法,与命令式语言中的自然方法相比,一旦你理解了它,它就是一种非常强大的思维方式。

作为第一个严肃的语言是 FORTRAN 的人(在那个时候甚至是正确的写它的名字的方式!)我认为这种归纳方法非常重要,这就是我想在这里表达的。

【讨论】:

  • 谢谢。这绝对是另一种看待问题的方式??
  • 这是这里最干净、不言而喻的正确代码(可能应该重新表述为基于do 的循环(4newbies:因为据说 CL 中没有 TCO 保证)但这只是一些语法戏法)。教训:不惜一切代价避免基于do* 的循环(4nb:因为时间问题很棘手)。我什至不得不花费我宝贵的互联网黄金来开发一个特别泥泞和骇人听闻的代码。
  • @WillNess:我在答案中附加了一条注释(比答案更长),解释了我试图理解的内容,并且这可能不是很自然的 CL。你是对的,对于生产使用它应该是do(或者loop,如果是我的话)。
【解决方案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)))

【讨论】:

  • 抱歉,这对我的口味来说太老套和泥泞了。证明它的正确性可能是一些大学课程的练习。不得不dv。 :(
【解决方案4】:

首先,如上所述,您应该使用do* 而不是do,以便按顺序绑定变量。 其次,您正在测试(null numbers) 以结束循环。但是,当只剩下一个元素要处理时,numbers 不是 nil,而是变成了 (cdr numbers),即 nil。这个 nil 值是作为第二个参数传递给 *smaller* 的值,这就是您收到错误消息的原因。为避免这种情况,您应该改为测试 (null (cdr numbers))

(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)))

当列表中只剩下一项时退出循环。

【讨论】:

  • 不要将函数命名为*name*。该约定是为特殊变量保留的。
猜你喜欢
  • 2020-09-07
  • 2020-06-07
  • 1970-01-01
  • 2013-12-18
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
  • 2016-09-08
  • 2019-03-04
相关资源
最近更新 更多