Lisp 使用相同的值调用多个函数

Lisp 使用相同的值调用多个函数,lisp,common-lisp,Lisp,Common Lisp,我有各种各样的函数,我想用相同的值调用每个函数。例如, 我有以下职能: (defun OP1 (arg) ( + 1 arg) ) (defun OP2 (arg) ( + 2 arg) ) (defun OP3 (arg) ( + 3 arg) ) 以及包含每个函数名称的列表: (defconstant *OPERATORS* '(OP1 OP2 OP3)) 到目前为止,我正在尝试: (defun TEST (argument) (dolist (n *OPERATORS*) (n arg

我有各种各样的函数,我想用相同的值调用每个函数。例如, 我有以下职能:

(defun OP1 (arg) ( + 1 arg) )
(defun OP2 (arg) ( + 2 arg) )
(defun OP3 (arg) ( + 3 arg) )
以及包含每个函数名称的列表:

(defconstant *OPERATORS* '(OP1 OP2 OP3))
到目前为止,我正在尝试:

(defun TEST (argument) (dolist (n *OPERATORS*) (n argument) ) )
我尝试过使用
eval
mapcar
apply
,但这些都不起作用

这只是一个简单的例子;我正在编写的程序有八个函数,它们是扩展搜索树中的节点所必需的,但目前,这个示例应该足够了

  • FUNCALL与符号一起工作
  • 来自愚蠢的把戏部门
  • (反常量*运算符*”(op1 op2 o3))
    (卸载测试(&rest参数)
    (setf(cdr arg)arg)
    (mapcar#'funcall*运算符*arg))
    
    其他答案提供了一些关于
    mapcar
    的惯用解决方案。有人指出,您可能需要一个函数列表(而
    *运算符*
    不是),而不是一个符号列表(而
    *运算符*
    是),但在公共Lisp中,将
    funcall
    作为符号是可以的。对于这一点,使用某种映射构造(例如,
    mapcar
    )可能更为常见,但由于您已经使用
    dolist
    提供了代码,因此我认为也值得研究如何以迭代的方式实现这一点。不过,让我们先讨论一下映射(可能更惯用)解决方案

    映射 您有一个固定的参数,
    argument
    ,您希望能够获取函数
    function
    ,并使用该参数调用它。我们可以将其抽象为一个函数:

    (defconstant *OPERATORS* '(OP1 OP2 OP3))
    
    (lambda(函数)
    (funcall函数参数)
    
    现在,我们想用您定义的每个操作调用这个函数。这很容易做到:

    (defun测试(参数)
    (地图车(lambda)(功能)
    (funcall函数参数)
    *操作员*)
    
    您还可以编写符号列表的
    (op1 op2 op3)
    (list'op1'op2'op3)
    ,或函数列表的
    (list#'op1#'op2#'op3)
    。所有这些工作都是因为将a作为其第一个参数,而函数指示符是

    一种表示函数的对象,它是以下对象之一:符号(表示在全局环境中由该符号命名的函数)或函数(表示自身)

    迭代地 您可以使用
    dolist
    执行此操作。的[文档]实际上显示了
    dolist
    还有一些技巧

    dolist(var列表形式[结果形式])声明*{tag | statement}*
    
    我们不需要担心这里的声明,也不需要使用任何标记,但是请注意可选的结果表单。您可以指定一个表单来生成
    dolist
    返回的值;您不必接受其默认值
    nil
    。在迭代循环中将值收集到列表中的常用习惯用法是
    推送将每个值反汇编到一个新列表中,然后返回该列表的
    反向
    。由于新列表不与任何其他列表共享结构,我们通常使用
    nreverse
    以破坏性方式反向。您的循环将变为

    (defun测试(参数)
    (让((结果“()))
    (dolist(op*运算符*(反向结果))
    (推送(funcop参数)结果)
    
    在风格上,我不喜欢只引入单个值的
    let
    ,并且可能会在函数中使用
    &aux
    变量(但这是一个品味问题,而不是正确性问题):

    (defun测试(参数和辅助(结果“()))
    (dolist(op*运算符*(反向结果))
    (推送(funcop参数)结果)
    
    您还可以方便地使用:

    (defun test2(参数)
    (op in*运算符的循环*
    collect(funcop参数)))
    
    您还可以使用以下方法进行简化,但可能不太容易理解:

    (defun test3a(参数)
    (do((结果’()(列表*(funcall(第一个运算符)参数)结果))
    (操作员*操作员*(其余操作员)))
    ((endp运算符)(nreverse结果)))
    

    这意味着在第一次迭代中,
    结果
    运算符
    分别用
    '()
    *运算符*
    初始化。当
    运算符
    为空列表时,循环终止,无论何时终止,返回值都是
    (nreverse results)
    。在连续迭代中,
    结果
    是一个指定的新值,
    (list*(funcall(第一个运算符)参数)结果)
    ,这就像
    将下一个值推到结果上一样,
    运算符
    更新为
    (剩余运算符)

    有一个库,在任何复杂的项目中几乎都是必需的:Alexandria。它有许多有用的功能,还有一些东西可以让你的代码更漂亮/更不冗长,更有意识

    比如说,您想调用多个具有相同值的函数。下面是您的方法:

    (ql:quickload "alexandria")
    
    (use-package :alexandria)
    
    (defun example-rcurry (value)
      "Calls `listp', `string' and `numberp' with VALUE and returns
    a list of results"
      (let ((predicates '(listp stringp numberp)))
        (mapcar (rcurry #'funcall value) predicates)))
    
    (example-rcurry 42) ;; (NIL NIL T)
    (example-rcurry "42") ;; (NIL T NIL)
    
    (defun example-compose (value)
      "Calls `complexp' with the result of calling `sqrt'
    with the result of calling `parse-integer' on VALUE"
      (let ((predicates '(complexp sqrt parse-integer)))
        (funcall (apply #'compose predicates) value)))
    
    (example-compose "0") ;; NIL
    (example-compose "-1") ;; T
    

    函数
    rcurry
    compose
    来自Alexandria软件包。

    这很聪明。花了一秒钟时间才看到你在那里用
    setf
    做了什么。(我通常看到
    (NCOC x)
    。但是,
    mapcar
    要求它的参数是正确的列表,因此使用循环列表调用它是未定义的行为。(不过,大多数实现都会按照您的预期执行。)另外,因为如果没有参数被传递到
    test
    ,那么
    setf
    就会失败。这是真的,但是有没有什么实现需要费心去检查呢?我实际上认为我已经使用了一个这样的实现,并启动了SBCL、CCL和CLISP来检查,但可能我弄错了。这不是一个精确的副本,但你可能会发现相关。多亏了e每个人,特别是约书亚·泰勒,为了纠正我糟糕的英语并给出详细的答案,我