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
,但这些都不起作用
这只是一个简单的例子;我正在编写的程序有八个函数,它们是扩展搜索树中的节点所必需的,但目前,这个示例应该足够了
(反常量*运算符*”(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每个人,特别是约书亚·泰勒,为了纠正我糟糕的英语并给出详细的答案,我