如何在LISP中定义一个传递两个表单然后随机执行的函数

如何在LISP中定义一个传递两个表单然后随机执行的函数,lisp,common-lisp,Lisp,Common Lisp,我是LISP新手,目前正在尝试定义一个函数,该函数将传递另外两个随机执行的表单。例如,如果我要执行任何表单,它会从一个选择中随机执行一个表单,该选择将返回结果 有人知道这方面的例子吗?我似乎对LISP了解不够,无法形成一个web搜索,以返回我要查找的结果。这需要任意数量的函数,并随机调用其中一个: (defun execute-one (&rest funcs) (let* ((random-pos (random (length funcs))) (func (n

我是LISP新手,目前正在尝试定义一个函数,该函数将传递另外两个随机执行的表单。例如,如果我要执行任何表单,它会从一个选择中随机执行一个表单,该选择将返回结果


有人知道这方面的例子吗?我似乎对LISP了解不够,无法形成一个web搜索,以返回我要查找的结果。

这需要任意数量的函数,并随机调用其中一个:

(defun execute-one (&rest funcs)
  (let* ((random-pos (random (length funcs)))
         (func (nth random-pos funcs)))
    (funcall func)))

(execute-one
  (lambda () (print 3))
  (lambda () (print 5))
  (lambda () (print 10)))

在Barmar的回答中,提供给
executeone
的表单必须是
lambda
类型,并且是零参数。我建议将其作为一个宏来实现:

(defmacro execute-one (&body funcs)
  `(let* ((random-pos (random (length ',funcs)))
      (func (nth random-pos ',funcs)))
     (eval func)))
现在您可以提供任意表单,
executeone
将随机计算其中一个表单

例如:

(execute-one
  (+ 10 100)
  (* 234 934)
  (expt 2 100))

=> 110
编辑: 我已经能够从中驱除
eval

(defmacro execute-one (&body funcs)
  `(let* ((random-pos (random (length ',funcs)))
      (func (nth random-pos ',funcs)))
     (apply #'funcall func)))
@Rainer,除了
eval
,你能详细解释一下为什么它的风格不好吗?

用宏包装一个功能实现… 演示如何使用函数执行此操作。我认为这是解决这个问题最明智的实现技术。建议使用宏执行此操作。我认为这是解决这个问题的最自然的编程接口(不过,正如评论中所指出的,这个答案中的宏存在一些问题)。最后,我认为将这两种技术结合起来是值得的,这样您就可以从实现中获益(如果您愿意,您可以在以后使用函数对象),并且在以后的时间里使用一个方便的接口

(defun调用一(函数)
(funcall(n(随机(长度函数))函数)
(其中一种形式(&body forms)
`(呼叫一个(列表),@(地图车)(lambda(表格)
`(lambda()
,表格(
表格))
在我看来,这种类型的宏实现技术,主要功能作为函数实现,宏根据函数实现,在适用的情况下是良好的实践。将功能实现为函数通常更容易,因为您不太关心名称捕获、构造表单等问题。有时,将功能实现提供给您会很有用,因此您增加了一点灵活性。在函数方面实现宏意味着宏实际上是为您充当语法糖;从概念上讲,您所需要做的就是将一些单独的表单包装到匿名函数中,而这对于宏来说是一项并不太复杂的任务。当然,这并不适用于每一个宏,但在我看来,当它适用时,它会导致代码更易于维护,并且从一开始就没有那么多错误

但要避免重复工作… 不过,这里有一个值得注意的问题。展开中的一个,创建一个列表并将其传递给调用一个:

(macroexpand-1 '(one-of 3 4))
;=> (CALL-ONE (LIST (LAMBDA () 3) (LAMBDA () 4)))
callone
计算函数列表的长度并生成一个随机数。对于大多数调用来说,这很好,但对于扩展
之一来说并不太好,因为这意味着我们重新计算一个列表的长度,这个长度永远不会一次又一次地改变。当我们使用
中的一个时,我们可以在宏扩展时计算
(长度函数)
,但是我们仍然需要一种方法来提供它来调用一个
。因此,我们可以通过向默认为
(长度函数)
调用一个
添加可选参数来稍微更改函数接口。在之一的扩展中,我们只提供一个常量值

(defun call-one (functions &optional (len (length functions)))
  (funcall (nth (random len) functions)))

(defmacro one-of (&body forms)
  `(call-one (list ,@(mapcar (lambda (form)
                               `(lambda ()
                                  ,form))
                             forms))
             ,(length forms)))
因此我们得到了这个扩展:

(macroexpand-1 '(one-of 3 4))
;=> (CALL-ONE (LIST (LAMBDA () 3) (LAMBDA () 4)) 2)

大家好,谢谢你们的回复。他们正在帮助我更好地理解这一点。一旦我创造了一些东西,我会发布和展示。但我只是想说声谢谢你的快速回复。:)这个宏的样式不好。特别是因为它使用EVAL@Wojciech,所以格式化(缩进)也需要一些工作,但这不是一个严重的技术问题。更重要的是,这不起作用。(i) 最终的宏版本与您给出的第一个示例不一样。您可以
(eval'(+10100))
,但不能
(apply#'funcall'(+10100))
。(ii)如果其中一个代码块使用名为
random pos
的变量,会发生什么情况?(iii)虽然每次都需要生成一个随机数,但没有理由每次都调用
length
。由于所提供表单的数量是固定的,您可以在编译时计算
(length funcs)
。@JoshuaTaylor,至于(i),我实际上可以在SBCL中运行
(apply#'funcall'(+10100))
,并得到110的响应。你说的“不能”到底是什么意思@很抱歉,这个特定的示例确实有效,因为您可以执行
(funcall'+'10'100)
。但是,不能将
funcall
应用于任意表单并使其工作。例如,您不能执行
(应用#'funcall'(let((x 10))(+x 100))
,因为您不能执行
(funcall'let'((x 10))(+x 100))
。此外,您确实应该删除
eval
版本;它在很多情况下根本不起作用。例如,
(让((x 10))(执行一个x))
将失败。
(macroexpand-1 '(one-of 3 4))
;=> (CALL-ONE (LIST (LAMBDA () 3) (LAMBDA () 4)) 2)