Common Lisp中let*和set的区别是什么?

4
我正在进行一个遗传编程的业余项目。
我设置了一个函数/宏,当在setq/setf形式中进行评估时,将生成一个列表,看起来类似于这样。
(setq trees (make-trees 2)) 
==> (+ x (abs x))

然后,通过函数/宏的策略性使用,它将被绑定到一个lambda函数#<FUNCTION :LAMBDA (X) ... >
但是,我希望能够更加有效地进行操作,而不是手动分配变量,因此我编写了类似以下内容的代码:
(setq sample 
      (let* ((trees (make-trees 2))
         (tree-bindings (bind-trees trees))
         (evaluated-trees (eval-fitness tree-bindings))))
      (list (trees tree-bindings evaluated-trees)))

然而,当我把它放在let形式中时,会得到“EVAL:trees has no value”的错误。我怀疑在LET中,与SETF相比,宏展开没有完全执行,但这对我来说毫无意义。
导致这个问题的原因是什么?
---编辑:我复制了我的代码并将整个文件放在pastebin中---
假设我决定setq不能满足我的需求,并编写一个简单的函数来完成它:
(defun generate-sample () (let ((twiggs (make-trees 2))) (let ((tree-bindings (bind-trees twiggs))) (let ((evaluated-trees (eval-fitness tree-bindings))) (list twiggs tree-bindings evaluated-trees)))))
这会产生一系列的...帮助文件错误消息(??!?)...和“eval:variable twiggs has no value”,源于SLIME检查上的bind-trees定义。
我相当确定我已经完全弄乱了我的宏。http://pastebin.org/673619

我不太明白你的宏在做什么。你所谓的“函数/宏的战略性使用”是什么意思?还有所有这些顶层的 setq 是怎么回事? - Ken
嗯,一个setq有点像全局的“人口池”; 其他的只是开发过程中的产物。我的宏会根据相当随意的表达式树动态生成函数(想想就会变得“有点”复杂)。 - Paul Nathan
你的第二个片段是不完整的。你的let表单体内会发生什么?缩进也很误导,括号也不平衡。 - Svante
1个回答

3

(Setq make-trees 2)将变量make-trees的值设置为2,然后返回2。

根据您所描述的情况,我不认为需要使用宏。如果您的make-trees只是创建一棵随机树,并且可以被解释为一个程序,那么只需使用defun定义一个函数即可。我建议如下:

(defun make-tree (node-number)
  (if (= node-number 1)
      (make-leaf)
      (cons (get-random-operator)
            (mapcar #'make-tree
                    (random-partition (- node-number 1)))))) 
< p > Letsetq的作用完全不同。Setq将值分配给现有变量,而let创建一个具有多个词法绑定的新词法范围。

我认为你应该展示更多的代码;目前,你的问题并没有太多意义。


更新:

我会修复你片段的缩进以使事情更清晰:

(setq sample 
      (let* ((trees (make-trees 2))
             (tree-bindings (bind-trees trees))
             (evaluated-trees (eval-fitness tree-bindings))))
      (list (trees tree-bindings evaluated-trees)))

现在,正如之前所述,let*建立词法绑定。这些绑定只在其主体内有效:
(setq sample 
      (let* ((trees (make-trees 2))
             (tree-bindings (bind-trees trees))
             (evaluated-trees (eval-fitness tree-bindings)))
        ;; here trees, tree-bindings, and evaluated-trees are bound
        ) ; end of let* body
      ;; here trees, tree-bindings, and evaluated trees are not in scope anymore
      (list (trees tree-bindings evaluated-trees)))

最后一行也是无意义的。如果这些名称被绑定,它将返回一个元素的列表,该元素是使用tree-bindingsevaluated-trees作为参数评估trees函数的结果。

你可以像这样得到想要的结果:

(setq sample 
      (let* ((trees (make-trees 2))
             (tree-bindings (bind-trees trees))
             (evaluated-trees (eval-fitness tree-bindings)))
        (list trees tree-bindings evaluated-trees)))

另一个更新:

宏的目的是消除重复的代码,当使用函数无法实现这一点时。一个常见的应用场景是在处理位置时,还需要定义新的控制结构。只要您没有看到某些东西无法作为函数工作,请不要使用宏。

以下是一些可能有帮助的代码:

(defun make-tree-lambda (depth)
  (list 'lambda '(x)
        (new-tree depth)))

(defun make-tree-function (lambda-tree)
  (eval lambda-tree))

(defun eval-fitness (lambda-form-list input-output-list)
  "Determines how well the lambda forms approach the wanted function
by comparing their output with the wanted output in the supplied test
cases.  Returns a list of mean quadratic error sums."
  (mapcar (lambda (lambda-form)
            (let* ((actual-results (mapcar (make-tree-function lambda-form)
                                           (mapcar #'first input-output-list)))
                   (differences (mapcar #'-
                                        actual-results
                                        (mapcar #'second input-output-list)))
                   (squared-differences (mapcar #'square
                                                differences)))
              (/ (reduce #'+ squared-differences)
                 (length squared-differences))))
          lambda-form-list))

(defun tree-fitness (tree-list input-output-list)
  "Creates a list of lists, each inner list is (tree fitness). Input
is a list of trees, and a list of test cases."
  (mapcar (lambda (tree fitness)
            (list tree fitness))
          tree-list
          (eval-fitness (mapcar #'make-tree-lambda tree-list)
                        input-output-list)))

@Svante - 现在正在工作,无法获取代码 - 但是当我这样输入时,我得到了“trees不是一个值”的错误。 - Paul Nathan
@Paul Nathan:好的,如果我模拟你展示的内容,它不起作用。你试过逐步执行吗? - Svante
@Svante:我被困扰到了无法理解的地步。我的文件已经上传到pastebin链接中了。> . < - Paul Nathan
@Paul Nathan:我已经在你的pastebin上添加了一些注释。核心问题似乎是你不理解宏的作用。特别是你的bind-trees,以及eval-fitness对我来说相当难以理解。 - Svante
@Svante:感谢您对我Lisp的评论。我还在学习中。我正在尝试随机构造一个列表(一棵“树”),使用操作符列表和任意值X,然后将该“树”构建为匿名函数。给定随机生成的函数,我想使用一些样本值(eval-fitness)运行它,并查看它是否符合目标函数。我的下一个目标是交换树的分支,以尝试改进目标函数。我使用宏来基本上"粘贴"树到模板(lambda (X) tree)中。 - Paul Nathan
  • 改进目标函数
  • 改进随机生成函数
- Paul Nathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接