要在 Haskell 中对 Prolog 计算进行建模,您需要一个回溯 monad。这可以使用LogicT
monad 轻松完成。您的示例可以转换为以下内容:
import Control.Monad.Logic
myFunc :: Int -> [Int] -> Logic [Int]
myFunc field acc = ifte (exitCond field acc) (\_-> return acc) $
(do f <- nextField field
guard $ test1 f
myFunc f acc)
`mplus`
(do f <- nextField field
guard $ test2 f
myFunc f (f:acc))
假设函数和谓词的以下实现:
nextField i = return (i+1)
test1 f = f < 10
test2 f = f < 20
exitCond f a = guard (f > 15)
您使用mplus
组合Logic
计算,以便如果一个失败,它会回溯并尝试另一个。ifte
只是一个软切(没有硬切logict
,虽然我相信它是微不足道的,因为logict
它是基于延续的)在退出条件为真时退出。您按如下方式运行计算:
Main> runLogic (myFunc 1 []) (\a r -> a:r) []
[[16,15,14,13,12,11,10],[16,15,14,13,12,11,10,9],[16,15,14,13,12,11,10,8]...
runLogic
接受Logic
计算、延续和延续输出的初始值。在这里,我刚刚传递了一个延续,它将所有结果累积到一个列表中。与 Prolog 示例不同,上面将回溯并获得所有解决方案,因为我们使用了软切割而不是硬切割。要在获得第一个解决方案后停止回溯,您可以使用once
:
Main> runLogic (once $ myFunc 1 []) (\a r -> a:r) []
[[16,15,14,13,12,11,10]]
您也可以使用observe
仅观察第一个解决方案,而无需传递延续:
Main> observe (myFunc 1 [])
[16,15,14,13,12,11,10]
甚至obserMany
和observeAll
:
observeMany 5 (myFunc 1 []) --returns the first 5 solutions
observerAll (myFunc 1 []) --returns a list of all solutions
最后,您需要安装logict
软件包才能使上述代码正常工作。用来cabal install logict
安装它。
编辑:在评论中回答您的问题
是的,您可以执行类似的操作而无需安装logict
. 尽管专用的回溯 monad 使事情变得不那么复杂,并且清楚地说明了您要做什么。
要对logict
上面的示例进行建模,您只需要[]
monad
myFunc :: Int -> [Int] -> [[Int]]
myFunc field acc | exitCond field acc = return acc
myFunc field acc = do
let m1 = do
f <- nextField field
guard $ test1 f
myFunc f acc
m2 = do
f <- nextField field
guard $ test2 f
myFunc f (f:acc)
in m1 `mplus` m2
nextField i = return $ i + 1
exitCond i a = i > 15
test1 i = i < 10
test2 i = i < 20
您可以按如下方式运行它:
Main> myFunc 1 []
[[16,15,14,13,12,11,10],[16,15,14,13,12,11,10,9],[16,15,14,13,12,11,10,8]...
您还可以像以前一样选择所需的解决方案数量:
Main> head $ myFunc 1 []
[16,15,14,13,12,11,10]
Main> take 3 $ myFunc 1 []
[[16,15,14,13,12,11,10],[16,15,14,13,12,11,10,9],[16,15,14,13,12,11,10,8]]
但是,您将需要Cont
monad,因此需要ListT
monad 来实现如 Prolog 示例中的硬切,这在上面的示例中不可用logict
:
import Control.Monad.Cont
import Control.Monad.Trans.List
myFunc :: Int -> ListT (Cont [[Int]]) [Int]
myFunc field = callCC $ \exit -> do
let g field acc | exitCond field acc = exit acc
g field acc =
let m1 = do
f <- nextField field
guard $ test1 f
g f acc
m2 = do
f <- nextField field
guard $ test2 f
g f (f:acc)
in m1 `mplus` m2
g field []
与 Prolog 一样,最后一个示例在满足后不会再次回溯exitCond
:
*Main> runCont (runListT (myFunc 1)) id
[[16,15,14,13,12,11,10]]