我正在尝试寻找解决堆栈溢出问题的问题 alogrithm很清楚,但是递归调用是在if情况下进行的,所以我不知道为什么会变坏
(defparameter BF '(B C) )
(defparameter BR '((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 ) ))
(defun chain_av (BF BR F)
(loop
(unless (member F BF)
(dolist (R BR)
(when (actionable R BF)
(remove R BR)
(append BF (conclusion-part R)))
(if (member F BF) (return "yes")
(or(chain_av BF BR F) (return nil)))))))
(defun conclusion-part( r)
(caddr r)
)
(write (chain_av BF BR 'H ))
(defun actionable (r facts)
(let ((ok t))
(dolist (p (cadr r) ok)
(if (not (member p facts))
(setq ok nil)))
ok))
而且我仍然不确定它是否真的在第一次搜索之下 我应该通过拥有一个变量来证明该变量 并预先感谢
答案 0 :(得分:8)
以下是有关代码中一些错误的一般说明,然后是有关如何实现正向链接的提示。
正确设置代码格式很重要,否则您或您的同伴将无法轻松地读回代码。例如参见https://lisp-lang.org/style-guide/:
(defun conclusion-part( r)
(caddr r)
)
以上各行都有括号,这不是习惯用法。另外,Common Lisp具有一个名为THIRD
的功能,对于大多数人来说,它比CADDR
更易于理解。适当缩进并在括号末尾加括号。使用诸如Emacs之类的编辑器可以自动缩进代码:这可以帮助您识别出您写的内容与打算编写的内容不同的情况,因为自动缩进遵循列表结构,并且可以发现括号中的错误位置。
(defun conclusion-part (rule)
(third rule))
您所做的是定义一个conclusion-part
访问器函数来访问您的规则临时数据结构的一部分。具有一组与特定实现无关的访问器将有所帮助,这也是引入有意义的名称的好机会。您应该对规则的所有部分都执行相同的操作(您可以在其他地方直接使用CADR,这不是很干净)。
例如(随意查找更好的名称):
(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
一致。
返回true或false的函数称为谓词,按照惯例,在Lisp中后缀为-p
(在Scheme中后缀为-?
)。您可能会也可能不会遵循该规则,但是请引入更有意义的变量名。这是您可以如何重写的方法:
(defun actionablep (rule facts)
(dolist (term (rule-antecedent rule) t)
(unless (member term facts)
(return nil))))
由于您已经知道loop
,因此您也可以这样写:
(defun actionablep (rule facts)
(loop
:for term :in (rule-antecedent rule)
:always (member term facts)))
这里也有一些问题:
您不会使用REMOVE
或APPEND
的返回值,这些返回值保证不会改变其参数(与DELETE
和NCONC
不同,即使对于这些功能,仅返回值也很重要,授予对现有存储进行突变的能力是实现定义的,并且只有在此位置才能有效地重用内存。
在某个分支中,您想在另一个"yes"
中返回nil
; CL可能是动态类型的,但是这里不需要字符串返回值。
return
形式仅存在于最里面的nil
块中。在您的情况下,这意味着您从DOLIST
建立的隐式块中返回,而不是从LOOP
建立的隐式块中返回。您可以命名您的loop
,但实际上这不是必需的,您可以在没有return
的情况下编写整个内容。一般来说,您可以使用一种纯粹的功能方法。
我编写了一个forward-chaining
函数,并对其进行了跟踪;在REPL中:
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 ... ... ...)))))
换句话说:要么目标已经是已知事实,要么存在一个可行的规则,通过该规则可以传递目标。请注意,如果您的规则是非终止的,则可能具有无限递归。
访问规则的顺序确定您的策略是深度优先(始终遵循最后尝试的规则,并使用规则列表作为堆栈来遵循该规则)或广度优先(应用所有可激活规则)对于给定的事实,使用规则列表作为队列)。我不知道“先于先下”一词的来源,我从中找不到认真的参考文献(有一篇论文在提及在先于