【问题标题】:defun with a list as argument以列表为参数的 defun
【发布时间】:2012-01-21 03:04:41
【问题描述】:

我正在尝试选择 Lisp 作为我的新语言,但在解决如何让函数的某些部分对传递给它的列表的每个元素起作用时遇到了一些问题。

为了学习如何解决这个问题,我正在尝试编写一种相当基本的除法形式,当列表中的一个元素为 0 时不会发出嘶嘶声(而是只返回 0)

(defun divtest (elements)
  (dolist (x elements)
    (if (zerop x) 0 () )
  (/ elements)))))

我尝试将其运行为:

(divtest '(20 2 5))

产量:

*** - /: (20 2 5) is not a number

失败点似乎源于这样一个事实,即在将列表中的元素传递给函数之前,我没有“提取”它们(在这种情况下,/ 和 dolist 都没有按预期工作,因为 x 永远不会评估为0)。 如果我是对的,有人可以告诉我如何执行这种“提取”吗?


注意:这个问题与 one that I've asked earlier 有关,但由于我不清楚先前答案的哪一部分实际上允许它按预期工作以解决这个特定问题,我决定进一步深入基础知识

【问题讨论】:

    标签: lisp common-lisp


    【解决方案1】:

    / 将一个或多个数字作为参数,但在您的代码中,您传递给它一个列表 - 显然这不起作用。函数 apply 是你的朋友 - (apply #'foo a b (list c d e)) 相当于 (foo a b c d e)。注意apply 在要使用的函数和最终列表之间的参数是可选的,所以(apply #'/ '(20 2 5)) 等价于(/ 20 2 5)

    此外,您删除零的尝试也行不通。 dolist 正在为参数列表 elements 中的每个项目评估其正文,但实际上您并没有做任何事情来更改 elements 的内容(评估 dolist 的正文的结果不会重新分配给源您似乎期望的元素)。

    函数remove-if(及其破坏性对应物delete-if)正是您要寻找的。下面展示了如何使用它(它需要很多可选参数,您无需为此担心)。

    (defun divtest (elements)
      (apply #'/ (remove-if #'zerop elements)))
    

    另请注意,如果elements 列表的第一个元素为零(假设我了解该函数的用途),则此操作将不正确。所以你可能想要类似的东西

    (defun divtest (elements)
      (apply #'/ (first elements) (remove-if #'zerop (rest elements))))
    

    有关详细信息,请参阅 Hyperspec。

    【讨论】:

    • 只是好奇:那有什么用处?
    • 这样您可以处理任意长列表,而不仅仅是 CALL-ARGUMENT-LIMIT(标准 CL 常量)最大长度的列表。 Common Lisp 有一个依赖于实现的最大参数数量。这个数字必须是 50 或更大。这意味着一个实现只需要支持 50 个参数(或更多)。因此,您的上述函数在某些实现中可能会失败,其中数字列表长于调用函数 / 允许的参数数量。
    • 使用 REMOVE-IF “尝试处理第一个元素”没有任何警告,它是一个非破坏性功能。需要注意的是,纯粹依赖 DELETE-IF 的副作用,副作用破坏的列表仍将包含其头元素,因此您仍应将返回值分配给原始存储。
    【解决方案2】:

    或者你可以这样写

    (defun divtest (elements)
      (if (member 0 elements)
          0
          (apply #'/ elements)))
    

    【讨论】:

      【解决方案3】:
      (block exit
        (reduce #'/ '(1 2 3 0 5)
                :key (lambda (x)
                       (if (zerop x)
                           (return-from exit 0)
                         x))))
      

      【讨论】:

        【解决方案4】:

        尝试用(apply / elements) 代替(/ elements)。我认为(?)这应该适用于 Lisp 的大多数方言。

        【讨论】:

        • 您需要引用 /,除非您已将函数分配给 / 的数据单元格。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-10
        • 1970-01-01
        • 2011-08-01
        • 2015-08-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多