浅尝 minikanren,传说中的逻辑式编程(一)
本机安装了 Chez Scheme 9.5,上次用还是大前年,白驹过隙啊。下载了 The Reasoned Schemer 第二版代码,按首页的例程尝试如下:
> (run* 啥 (== '面 啥))
(面)
啥和面一样呢?就是“面”
> (run* q (*o (build-num 3) (build-num 4) q))
((0 0 1 1))
看看 build-num 啥意思先:
> (build-num 3)
(1 1)
> (build-num 4)
(0 0 1)
应该就是二进制,高位在后,(0 0 1 1)就是 12,那么 *o 就是乘法
看来 minikanren(下称“小关”,因为根据 wiki:The name kanren comes from a Japanese word (関連) meaning “relation”. )环境搭成了,下面走一遍 minikanren 官网 上的 交互式入门。
> (fresh (甲 乙 丙) (== 甲 丙) (== 3 乙))
#<procedure at trs2-impl.scm:2941>
这里声明了:甲和丙相等、乙和 3 相等两个关系,但并未指定求啥。
> (run 1 (啥) (fresh (甲 乙 丙) (== 甲 丙) (== 3 乙)))
((_0))
这里“啥”并未与上面两个关系扯上联系,因此可以是任何值,表示为 _0。暂时不知那个 1 何用。也不知为何比教程里多了一层括号。
下面这样仍然求不得,因为乙是 3 但甲不知是几:
> (run 1 (啥) (fresh (甲 乙) (== 甲 啥) (== 3 乙)))
((_0))
下面这样就有值了,不过甲和丙相等这层关系实际上没用。
> (run 1 (啥) (fresh (甲 丙) (== 甲 丙) (== 3 啥)))
((3))
下面的关系就都用上了:
> (run 1 (啥) (fresh (甲 丙) (== 甲 丙) (== 3 丙) (== 啥 甲)))
((3))
这样的话,里面那层的 fresh 就没用:
> (run 1 (啥)
(fresh (甲 啥)
(== 4 甲)
(== 甲 啥))
(== 3 啥))
((3))
如果关系有矛盾的话,返回空:
> (run 1 (啥) (== 4 3))
()
> (run 1 (啥) (== 5 啥) (== 6 啥))
()
conde 让多套关系之一满足即可:
> (run 2 (啥)
(fresh (某 甲 乙)
(conde
((== `(,甲 ,某 ,甲) 啥)
(== 乙 某))
((== `(,某 ,甲 ,某) 啥)
(== 乙 某)))))
(((_0 _1 _0)) ((_0 _1 _0)))
0 和 1 是按 甲/某 的顺序指派的. 2 是指最多两个答案. 如果用 run*, 就没有上限, 但容易死循环, 如下:
> (run* (啥)
(let 循环 ()
(conde
((== #f 啥))
((== #t 啥))
((循环)))))
暂不明白这个 let 啥用, 如果改用 run 加指定答案上限数,应该就不会死循环了:
> (run 9 (啥)
(let 循环 ()
(conde
((== #f 啥))
((== #t 啥))
((循环)))))
居然还是死循环!? 开了 issue: https://github.com/webyrd/webmk/issues/4
今天先到此吧.