1

我正在用 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).

在哈斯克尔?上面的代码正在检查一些条件并递归调用自身,所以最后我得到了特定字段的列表。关键是,如果某些条件(test1 或 test2)失败,它会返回到最后一点,它可以做出其他选择并执行。我如何在 Haskell 中实现类似的东西?

4

4 回答 4

4

要在 Haskell 中对 Prolog 计算进行建模,您需要一个回溯 monad。这可以使用LogicTmonad 轻松完成。您的示例可以转换为以下内容:

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

但是,您将需要Contmonad,因此需要ListTmonad 来实现如 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]]
于 2013-06-22T16:54:23.667 回答
2

您的评论有助于澄清一些问题,但是对于您要查找的内容仍有一些疑问,因此这里是使用列表单子和守卫的示例。

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]
于 2013-06-21T19:35:47.400 回答
0

我从来没有在 Haskell 中编程过——然后我会寻求你的帮助——但可以暗示

该Prolog片段-我认为您有错字-应该可以myFunc(Field2, [(X1,Y1)|Acc], Result).手动编译-在延续传递模式中。

让我们谷歌一下(haskell continuation pass prolog)。我将首先查看Wikipedia 页面:在 Haskell 附近,我们找到了continuation monad

现在我们可以尝试在可执行的 Haskell 中翻译该 Prolog。这有意义吗?

于 2013-06-22T13:18:41.303 回答
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)    )))
于 2013-06-22T21:32:04.067 回答