Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 如何使用列表单子计算/表示非确定性计算的结果?_Haskell_Monads_Non Deterministic - Fatal编程技术网

Haskell 如何使用列表单子计算/表示非确定性计算的结果?

Haskell 如何使用列表单子计算/表示非确定性计算的结果?,haskell,monads,non-deterministic,Haskell,Monads,Non Deterministic,我想构造一个计算,其中上下文是指向当前的所有路径的历史(形成一棵树),函数是以过去状态为条件的当前状态。函数本身是不确定的,因此一个过去的状态可能导致几个未来的状态,因此树分支。将此计算的结果表示为一棵树是有意义的,但是有没有一种方法可以用列表单子简洁地表示它呢?或者其他一些我不知道的构造?使用列表单子可以让你像树一样构造计算,但它会丢失源信息。最后,您将有一个结果列表,但您不知道每个结果来自何处 如果这就是你所关心的,那么单子列表就是完美的。假设您有一个非确定性的步骤函数: step :: S

我想构造一个计算,其中上下文是指向当前的所有路径的历史(形成一棵树),函数是以过去状态为条件的当前状态。函数本身是不确定的,因此一个过去的状态可能导致几个未来的状态,因此树分支。将此计算的结果表示为一棵树是有意义的,但是有没有一种方法可以用列表单子简洁地表示它呢?或者其他一些我不知道的构造?

使用列表单子可以让你像树一样构造计算,但它会丢失源信息。最后,您将有一个结果列表,但您不知道每个结果来自何处

如果这就是你所关心的,那么单子列表就是完美的。假设您有一个非确定性的
步骤
函数:

step :: State -> [State]
如果我们想一步一步地进行,我们可以写下如下内容:

startState >>= step >>= step >>= step
这将在3个步骤后为我们提供所有可能的结果。如果我们想将其推广到任何数字,我们可以使用一元复合运算符
(b
)编写一个简单的辅助函数。它可能看起来像这样:

stepN :: Int -> (State -> [State]) -> State -> [State]
stepN n f = foldr (<=<) return (replicate n f)
traverse :: Tree a -> NonDet Direction a
traverse (Leaf x)  = return x
traverse (Bin l r) = (L #> traverse l) `mplus` (R #> traverse r)
stepN::Int->(State->[State])->State->[State]

stepN f=foldr(我想在Tikhon Jelvis的回答中补充一点,如果需要跟踪执行分支的方式,可以使用更复杂的monad堆栈组合。例如:

import Control.Monad
import Control.Monad.Writer
import Data.Sequence

-- | Represents a non-deterministic computation
-- that allows to trace the execution by sequences of 'w'.
type NonDet w a = WriterT (Seq w) [] a
WriterT(Seq w)[]A
的值在
[(A,Seq w)]
中,即一个可能结果的列表,每个结果都包含一系列类型为
w
的标记。我们使用这些标记跟踪我们的步骤

我们首先创建一个助手函数,该函数只向当前执行跟踪添加一个标记:

-- | Appends a mark to the current trace.
mark :: w -> NonDet w ()
mark = tell . singleton
也许还有一个更方便的函数,可以添加一个标记,然后进行给定的计算:

-- | A helper function appends a mark and proceeds.
(#>) :: w -> NonDet w a -> NonDet w a
(#>) x = (mark x >>)
作为一个非常简单的例子,假设我们想要遍历一棵树

data Tree a = Leaf a | Bin (Tree a) (Tree a)
(在现实中,当然不会有树,分支将由复杂的东西决定。)

我们将记住我们通过一系列方向所走过的路径

data Direction = L | R
  deriving (Show, Read, Eq, Ord, Enum, Bounded)
我们的遍历函数将如下所示:

stepN :: Int -> (State -> [State]) -> State -> [State]
stepN n f = foldr (<=<) return (replicate n f)
traverse :: Tree a -> NonDet Direction a
traverse (Leaf x)  = return x
traverse (Bin l r) = (L #> traverse l) `mplus` (R #> traverse r)
召唤

runWriterT $ traverse $ Bin (Bin (Leaf "a") (Leaf "b")) (Leaf "c")
产生于

[("a",fromList [L,L]),("b",fromList [L,R]),("c",fromList [R])]
注意事项:

  • 请注意使用
    mplus
    来分支一元计算。使用
    mplus
    mzero
    更方便(或派生
    msum
    mfilter
    guard
    等)从
    MonadPlus
    ,而不是直接使用列表操作。如果以后更改monad堆栈,例如从
    []
    更改为我们的
    非设置方向
    ,则现有代码将无需修改即可工作
  • 对于
    WriterT
    我们可以使用任何幺半群,而不仅仅是序列

    type NonDet a = WriterT (Sum Int) [] a
    
    mark :: NonDet w ()
    mark tell (Sum 1)
    
    然后调用
    标记
    只会增加我们的计数器,调用的结果(稍微修改的
    遍历
    )将是


实际上,您可以比其他建议的解决方案做得更好。您可以为每个后续分支保留独立的历史记录,并实时跟踪执行路径

下面是您如何使用
pipes-4.0.0
(目前仍在Github上)实现此功能的:

通常,如果要保存当前访问状态的上下文,您将使用:

StateT [node] [] r
…其中,
node
是您访问过的地方。
StateT
跟踪您访问的每个节点,
[]
是非确定性部分。但是,如果您想添加效果,您需要将
[]
替换为monad transformer等效项:
ListT

StateT [node] (ListT IO) r
这就是派生
分支
类型的方法。在我们的特殊情况下,我们正在访问的
节点
s是
Int
s,并且
分支
在它所采用的每个路径的末尾返回当前上下文

当您
evalStateT
使用空的初始上下文时,您会得到:

evalStateT (branch 3) [] :: ListT IO [Int]
这是一个不确定的计算,它将尝试每个分支,在执行过程中跟踪
IO
中的结果,然后在结果末尾返回本地上下文。将有8个最终结果,因为我们的
分支将总共占用8条路径

如果我们使用
runRespondT
运行它,我们会得到一个
生产者

pipe :: () -> Producer' [Int] IO ()
P.print :: () -> Consumer [Int] IO r

pipe >-> P.print :: () -> Effect IO ()
此生成器将在到达每个执行路径的末尾时发出结果,并在执行过程中进行跟踪。我们不必等到计算结束后才能看到跟踪。我们只需查看它正在输出的
[Int]
就可以将其连接到
使用者上:

pipe :: () -> Producer' [Int] IO ()
P.print :: () -> Consumer [Int] IO r

pipe >-> P.print :: () -> Effect IO ()
这将我们的最终计算转换为基本monad中的
效果
(在本例中为
IO
)。我们可以使用
runProxy
运行此效果:

runProxy $ (pipe >-> P.print) () :: IO ()

然后跟踪计算并打印出每条路径的终点。

您认为可以使用列表理解来清楚地表达您的功能吗?列表单子几乎是一样的,但语法略有不同。它可能(也可能不是)看看丹·皮波尼在他的著作中的单子列表草图,你可以清楚地看到计算分支。是的,我认为
stepN::Int->(State->[State])->[State]
stepN
nf=`