【问题标题】:Find max in lisp在 lisp 中查找最大值
【发布时间】:2021-12-12 06:41:09
【问题描述】:

我正在尝试使用递归方法来查找列表中的最大值。 谁能解释一下我在这段代码中犯的错误以及下次如何处理它。

(defun f3 (i)
  (setq x (cond (> (car (I)) (cdr (car (I))))
                (f3 (cdr (I)))))
  )

(f3  '(33 11 44  2) )

我也尝试了以下方法,但没有奏效:

(defun f3 (i)
  (cond ((null I )nil )
        (setq x (car (i))
              (f3(cdr (i)))
              (return-from max x)
              )

非常感谢您的帮助。如果有帮助,我来自 java。

【问题讨论】:

  • 你的cond语法全错了。
  • x 变量有什么用?
  • (return-from max x) -- 你没有名为max的区块,你的意思是f3吗?
  • (car (I)) 应该是 (car i)。你把它和什么比较?
  • 这里有太多错误要修复它而不从头开始重写整个事情。

标签: lisp


【解决方案1】:

如果您使用 Common Lisp,那么您可以这样做:

(defun max-item (list)
  (loop for item in list
        maximizing item))

就是这样。 loopmaximizing item 子句确定看到的最高 item 值,并在它终止时隐含地将其确定为 loop 的结果值。

请注意,如果list 为空,则返回nil。如果你想要一些其他的行为,你必须在:

(if list
  (loop for item in list
         maximizing item))
  (... handle empty here ...))

如果已知列表中的元素数量很少,低于您的 Lisp 实现对可传递给函数的参数数量的限制,您可以简单地将列表应用于 max 函数:

(defun max-item (list)
  (apply #'max list))

如果list 为空,则max 被滥用:它需要一个或多个参数。可能会发出错误情况的信号。如果这在您的情况下不起作用,您需要添加代码以提供所需的行为。

如果预计列表很大,因此要避免这种方法,可以使用reduce,将max视为二进制函数:

(defun max-item (list)
  (reduce #'max list))

关于空list 的相同评论。这些表达式太小了,很多程序员会避免编写函数而直接使用它们。

关于递归,你不会在生产代码中使用递归来解决这个问题,只是作为学习递归的家庭作业。

【讨论】:

  • 非常感谢您的解释
【解决方案2】:

您正在尝试计算列表的最大值,因此请将您的函数命名为maximum 和您的参数list,而不是f3i。您不能将函数命名为 max 而不必考虑如何避免隐藏标准的 max 函数,因此现在最好忽略包问题并使用不同的名称。

当列表为空时,需要考虑一个极端情况,因为没有返回有意义的值。例如,您必须决定是返回 nil 还是发出错误信号。

骨架是这样的:

(defun maximum (list)
  (if (null list)
      ...
      ...))

注意右括号如何从不前加空格(或换行符),括号从不 > 按空格(或换行符)。另请注意,缩进会随着当前深度的增加而增加。这是 Lisp 格式化的基本规则,其他开发者请尝试遵循。

(setq x <value>)

你正在分配一个未知的地方x,如果你想要一个临时变量,你应该绑定一个新的变量,比如:

(let ((x <value>))
  <body>)

使用上述表达式,x 绑定到 &lt;value&gt; 内部 &lt;body&gt;(一个或多个表达式),并且仅存在于此。

(car (i))

与 Java 不同,括号不用于对表达式进行分组以提高可读性或强制某些评估顺序,在 Lisp 中它们包含复合形式。上面,在正常的评估上下文(不是宏或绑定)中,(i) 表示 调用函数 i,并且此函数与您的局部变量 i 无关(就像在 Java , 你可以写int f = f(2)f 表示一个变量和一个方法)。

如果你想接icar,写(car i)

您似乎将cond 用作某种if

 (cond (<test> <then>) <else>) ;; WRONG

您可以拥有if,如下所示:

 (if <test> <then> <else>)

例如:

(if (> u v) u v) ;; evaluates to either `u` or `v`, whichever is greater

cond 语法有点复杂,但您还不需要它。

你不能return-from一个未声明的块,你可能将函数重命名为f3而不重命名该部分,或者从其他地方复制它,但无论如何return-from只有当你有一个更大的时候才需要功能,可能还有更多的副作用。在这里,计算可以以更实用的方式编写。在类似 Lisp 的语言中有一个隐式的 return,这与 Java 不同,例如,在 add 中的最后一个(但也是单个)表达式下面计算为函数的返回值:

(defun add-3 (x) 
  (+ x 3))

从较小的示例开始并经常测试,在尝试做更复杂的事情之前修复编译器或解释器打印的任何错误。此外,请查看可用的在线资源以了解有关该语言的更多信息:https://common-lisp.net/documentation

【讨论】:

  • 非常感谢您的解释
【解决方案3】:

虽然其他答案是正确的:你肯定需要学习更多的 CL 语法,而且你可能不会在惯用的 CL 中递归地解决这个问题(无论'惯用的 CL' 是什么),这里是如何实际做到这一点,因为考虑如何递归地解决这些问题很有用。

首先让我们编写一个函数max/2,它返回两个数字的最大值。这很简单:

(defun max/2 (a b)
  (if (> a b) a b))

现在的诀窍是:假设您对数字列表的最大值有所了解:将此猜测称为m。那么:

  • 如果列表为空,则最大值为m
  • 否则列表有第一个元素,因此选择一个新的m,它是列表的第一个元素和当前m 中的最大值,然后对列表的其余部分进行递归。

所以,我们可以编写这个函数,我称之为max/carrying(因为它“携带”了m):

(defun max/carrying (m list)
  (if (null list)
      m
    (max/carrying (max/2 (first list) m)
                  (rest list))))

这几乎就是我们所需要的。然后诀窍是在max/carrying 周围写一个小垫片来引导它:

计算列表的最大值:

  • 如果列表为空,则没有最大值,这是一个错误;
  • 否则结果是列表第一个元素和列表其余部分的max/carrying

我不会写,但它很容易(发出错误信号,你想要的函数是error)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 2011-03-06
    • 2019-12-17
    • 2017-12-17
    • 1970-01-01
    相关资源
    最近更新 更多