Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.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_Haskell Pipes - Fatal编程技术网

Haskell 用管道编写一个简单的累加器';书写器

Haskell 用管道编写一个简单的累加器';书写器,haskell,haskell-pipes,Haskell,Haskell Pipes,使用pipes库,我想编写一个程序,从某个源读取数据,并进行单向累积(例如,使用Sum)。最简单的方法是 导入控件。代理为 导入数据。幺半群(总和) main=do 让source=enumFromToS(0::Int)5 a->foldD Sum 打印 当然,虽然这适用于小型源,但由于WriterT累加器的惰性,大型输入将导致可怕的堆栈溢出 谢天谢地,pipes似乎预见到了这一点,提供了一个具有严格累加器的WriterP代理。不幸的是,围绕这个代理的文档非常稀少。经过一番探索(并将问题简化为

使用pipes库,我想编写一个程序,从某个源读取数据,并进行单向累积(例如,使用
Sum
)。最简单的方法是

导入控件。代理为
导入数据。幺半群(总和)
main=do
让source=enumFromToS(0::Int)5
a->foldD Sum
打印
当然,虽然这适用于小型源,但由于
WriterT
累加器的惰性,大型输入将导致可怕的堆栈溢出

谢天谢地,
pipes
似乎预见到了这一点,提供了一个具有严格累加器的
WriterP
代理。不幸的是,围绕这个代理的文档非常稀少。经过一番探索(并将问题简化为每个下游元素累积1),我来到了这个程序

import-Control.Proxy
导入控制.Proxy.Trans.Writer
导入数据。幺半群(总和)
main=do
让source=enumFromToS(0::Int)5
a->\x->tell(Sum 1::Sum Int)
打印
当然,这个程序甚至不能正确执行简化任务,因为它累加到1而不是6。如果我没有弄错的话,这种行为可以解释为管道在终止之前只读取一个元素。为了重复直到输入结束,我得出以下结论:

import-Control.Proxy
导入控制.Proxy.Trans.Writer
导入数据。幺半群(总和)
main=do
让source=enumFromToS(0::Int)5
一倍之和
打印
折叠::(单子m,代理p,幺半群w)=>(a->w)->a'->写p a'a'a m r
折叠f=开始

其中go x=do a请记住,代理转换器在本地运行,而基monad在全局运行。这意味着
WriterP
让每个代理维护自己的累加器,首先终止的代理决定返回哪个累加器。累加器返回
0
的原因是您的枚举管道首先返回,而它没有累计任何内容

WriterP
折叠之所以严格,只是因为我可以控制该类型(而不是
transformers
中的类型)。我从来没有打算让它成为《代理前奏曲》中懒洋洋的褶皱的严格替代品。正确的严格替代方法是
foldlD'

请注意,
foldlD'mappend
基本上是一个严格的
Writer
折叠,特别是当您使用
mempty
作为初始状态运行基本
State
monad时

在您的情况下,您可以更轻松地使用:

main = do
    let source = enumFromToS (0::Int) 5
    a <- (`runStateT` 0) $ runProxy $ source >-> foldlD' (+)
    print a
main=do
让source=enumFromToS(0::Int)5
a->foldlD'(+)
打印

这将严格折叠输入。

我添加了一个新的答案,因为我已经在
pipes-3.3
中解决了这个问题,我刚刚上传到Hackage。管道背后的理论表明,您所期望的全局行为始终是正确的行为,
WriterP
现在表现为全局行为,因此您可以在管道中折叠

我已经修改了您的示例,以显示您将使用
pipes-3.3
实现它:

import Control.Proxy
import Control.Proxy.Trans.Writer

main = do
    let source = enumFromToS (0::Int) 5
    a <- runProxy $ execWriterK $ source >-> sumD
    print a
import-Control.Proxy
导入控制.Proxy.Trans.Writer
main=do
让source=enumFromToS(0::Int)5
a->sumD
打印
现在还可以检索管道中的折叠结果。例如,这是完全有效的:

chunksOf :: (Monad m, Proxy p) => Int -> () -> Pipe p a [a] m r
chunksOf n () = runIdentityP $ forever $ do
    -- The unitU discards the values that 'toListD' reforwards
    as <- execWriterK (takeB_ n >-> toListD >-> unitU) ()
    respond as
chunksOf::(Monad m,Proxy p)=>Int->()->Pipe pa[a]m r
chunksofn()=runIdentityP$forever$do
--unitU丢弃“toListD”重新前进的值
as->toListD>->unitU)()
回应为
下面是一个示例用法:

>>> runProxy $ getLineS >-> takeWhileD (/= "quit") >-> chunksOf 3 >-> printD
1<Enter>
2<Enter>
3<Enter>
["1","2","3"]
4<Enter>
5<Enter>
6<Enter>
["4","5","6"]
quit
>>runProxy$getLineS>->takeWhileD(/=“quit”)>->chunksof3>->printD
1.
2.
3.
["1","2","3"]
4.
5.
6.
["4","5","6"]
退出

很抱歉第一次答错了

您正试图通过
管道
重新实现
foldD
sumD
。查看源代码应该可以澄清您的疑问。@C.A.McCann它没有足够严格地折叠东西。当我有更多的时间时,我计划要求在《变形金刚》中添加一个更严格的版本。@GabrielGonzalez:啊,现在我看看源代码,我明白你的意思了。我想,@C.A.McCann,
Control.Monad.Trans.Writer.strict
的“严格性”(或缺乏严格性)在过去曾多次让我绊倒。我的印象是,甚至不可能编写一个严格累加的(例如,强制执行
mappend
Writer
单子,因为您没有任何可以
seq
@bgamari:是的。在查看源代码后,我现在意识到“严格”版本本质上是幼稚的实现,而“懒惰”版本添加了无可辩驳的模式,使其更加不严格。感谢更新!我将很快尝试合并此方法。