我试图找到解决堆栈流量问题的问题alogrithm很清楚,但它和递归调用是在一个if satetment中进行的,所以我无法弄清楚它为什么会变坏
(…
以下是有关代码中某些错误的一般性说明,后面是有关如何实现正向链接的提示。
正确格式化代码非常重要,否则您或您的同行将无法轻松阅读。例如,参见 https://lisp-lang.org/style-guide/ :
(defun conclusion-part( r) (caddr r) )
上面有各自的括号,这不是惯用语。另外,Common Lisp有一个名为的函数 THIRD 这比比较容易理解 CADDR 对大多数人来说。正确缩进并将括号放在最后。使用像Emacs这样的编辑器可以自动缩进代码:这有助于识别您编写的内容与您要编写的内容不同的情况,因为自动缩进遵循列表结构并可以帮助发现错位的括号。
THIRD
CADDR
(defun conclusion-part (rule) (third rule))
你做得好的是定义一个 conclusion-part 访问器功能访问您的部分 规则 临时数据结构。拥有一组与特定实现无关的独特访问器会有所帮助,并且是引入有意义名称的好机会。您应该对规则的所有部分执行相同操作(您在其他地方直接使用CADR,这不是很干净)。
conclusion-part
例如(随意找到更好的名字):
(defun rule-name (rule) (first rule)) (defun rule-antecedent (rule) (second rule)) (defun rule-consequent (rule) (cddr rule))
请注意 rule-consequent 是我改写的 conclusion-part ,除了它总是返回一个包含单个元素的列表(你能明白为什么吗?)。这在你打电话后很有用 append ,这是一致的 rule-antecedent 返回一个列表。
rule-consequent
append
rule-antecedent
返回true或false的函数称为谓词,并按约定为后缀 -p 在Lisp(和 -? 在Scheme)。您可能会也可能不会遵循该规则,但请引入更有意义的变量名称。以下是如何重写它:
-p
-?
(defun actionablep (rule facts) (dolist (term (rule-antecedent rule) t) (unless (member term facts) (return nil))))
既然你已经知道了 loop ,你也可以写:
loop
(defun actionablep (rule facts) (loop :for term :in (rule-antecedent rule) :always (member term facts)))
这里也存在一些问题:
你不要使用返回值 REMOVE 要么 APPEND ,这些函数可以保证不会改变它们的参数(不像 DELETE 和 NCONC ,即使对于那些函数,只有返回的值很重要,授予改变现有存储的能力是实现定义的,只有那里才能有效地重用内存。
REMOVE
APPEND
DELETE
NCONC
在某些分支中,您想要返回 "yes" , 在另一个 nil ; CL可能是动态类型的,但这里不需要字符串返回值。
"yes"
nil
一个 return 形式只存在于最里面 nil 块。在你的情况下,这意味着你从建立的隐式块返回 DOLIST 而不是那个 LOOP 。你可以说出你的名字 loop 但实际上这里没有必要,你可以不用写整篇文章 return 。更一般地说,您可以使用纯粹的功能方法。
return
DOLIST
LOOP
我写了一篇 forward-chaining 功能,并追溯它;在REPL中:
forward-chaining
CL-USER> (trace forward-chaining)
以下是我测试实现的方法:
(forward-chaining '(B C) '((R1 (B D E) F) (R2 (D G) A) (R3 (C F) A) (R4 (C) D) (R5 (D) E) (R6 (A) H) (R7 (B) X) (R8 (X C) A)) 'H)
我正在使用SBCL测试代码,实际输出可能在您的Lisp实现中有所不同:
0: (FORWARD-CHAINING (B C) ((R1 (B D E) F) (R2 (D G) A) (R3 (C F) A) (R4 (C) D) (R5 (D) E) (R6 (A) H) (R7 (B) X) (R8 (X C) A)) H) 1: (FORWARD-CHAINING (B C D) ((R1 (B D E) F) (R2 (D G) A) (R3 (C F) A) (R5 (D) E) (R6 (A) H) (R7 (B) X) (R8 (X C) A)) H) 2: (FORWARD-CHAINING (B C D E) ((R1 (B D E) F) (R2 (D G) A) (R3 (C F) A) (R6 (A) H) (R7 (B) X) (R8 (X C) A)) H) 3: (FORWARD-CHAINING (B C D E F) ((R2 (D G) A) (R3 (C F) A) (R6 (A) H) (R7 (B) X) (R8 (X C) A)) H) 4: (FORWARD-CHAINING (B C D E F A) ((R2 (D G) A) (R6 (A) H) (R7 (B) X) (R8 (X C) A)) H) 5: (FORWARD-CHAINING (B C D E F A H) ((R2 (D G) A) (R7 (B) X) (R8 (X C) A)) H) 5: FORWARD-CHAINING returned (H) 4: FORWARD-CHAINING returned (H) 3: FORWARD-CHAINING returned (H) 2: FORWARD-CHAINING returned (H) 1: FORWARD-CHAINING returned (H) 0: FORWARD-CHAINING returned (H)
该计划的基本结构是:
(defun forward-chaining (rules facts goal) (or ... (loop for ... in ... thereis (and (actionablep ... ...) (forward-chaining ... ... ...)))))
换句话说:要么目标已经是已知的事实,要么存在一个可行的规则,通过该规则可以传递目标。请注意,如果规则是非终止的,则可能会有无限递归。
您访问规则的顺序确定您的策略是否为深度优先(始终遵循最后一次尝试的规则并从该规则开始,使用规则列表作为堆栈),或广度优先(使用规则列表应用给定事实的所有可激活规则)作为一个队列)。我不知道“第一次搜索”这个术语来自哪里,我没有发现它的严重参考(有一篇论文引用 莱森森& Schardl 2010 在谈论时 下面 先搜索,但是 参考文章 只是没有提到它 广度优先 ,这是众所周知的)。