我在Haskell重写我的Prolog程序,我遇到了小问题,我怎么能做那样的事呢
myFunc(Field, Acc, Acc) :-
% some "ending" condition
!.
myFunc(Field, Acc, Result) :-
nextField(Field, Field2),
test1(Field2,...),
myFunc(Field2, Acc, Result).
myFunc(Field, Acc, Result) :-
nextField(Field, Field2),
test2(Ak, (X1, Y1)),
myFunc(Field2, [Field2|Acc], Result).
在Haskell?上面的代码检查某些条件并且recursivly调用自身,所以最后我得到特定字段的列表。重点是,如果某些条件(test1或test2)失败,它将返回到它可以做出其他选择的最后一点并执行它。我如何在Haskell中实现类似的东西?
答案 0 :(得分:4)
要在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]]
答案 1 :(得分:2)
你发表的评论有助于澄清一些问题,但是对于你正在寻找的内容仍有一些问题,所以这里有一个使用列表monad和guard的例子。
import Control.Monad
myFunc lst = do
e <- lst
guard $ even e -- only allow even elements
guard . not $ e `elem` [4,6,8] -- do not allow 4, 6, or 8
return e -- accumulate results
用于ghci:
> myFunc [1..20]
[2,10,12,14,16,18,20]
答案 2 :(得分:0)
我从来没有在Haskell编程 - 然后我会打电话给你帮助 - 但是可能暗示
Prolog片段 - 我认为你有一个拼写错误 - 应该是myFunc(Field2, [(X1,Y1)|Acc], Result).
可以手工编译 - 在继续传递模式中。
让我们google关于它(haskell继续传递prolog)。我将首先查看Wikipedia page:在Haskell附近找到continuation monad。
现在我们可以尝试在可执行的Haskell中翻译Prolog。这有意义吗?
答案 3 :(得分:0)
您的代码到Haskell的文本翻译是:
myFunc field acc = take 1 $ -- a cut
g field acc
where
g f a | ending_condition_holds = [a]
g f a =
( nextField f >>= (\f2 ->
(if test1 ... -- test1 a predicate function
then [()]
else [] ) >>= (_ ->
g f2 a )))
++
( nextField f >>= (\f2 ->
test2 ... >>= (\_ -> -- test2 producing some results
g f2 (f2:a) )))