Prologs返回haskell

时间:2013-06-21 15:56:39

标签: haskell prolog

我在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中实现类似的东西?

4 个答案:

答案 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]

甚至obserManyobserveAll

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)    )))