Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.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 如何使用高阶函数实现这种基于IO的循环?_Haskell_Higher Order Functions - Fatal编程技术网

Haskell 如何使用高阶函数实现这种基于IO的循环?

Haskell 如何使用高阶函数实现这种基于IO的循环?,haskell,higher-order-functions,Haskell,Higher Order Functions,我有一些类似以下的代码,它根据从磁盘读取的随机样本更新状态: myloop 0 state = return state myloop n state = do sample <- getRandomSampleFromFile myloop (n - 1) (process state sample) myloop 0状态=返回状态 myloop n state=do 样本 您可以使用应用程序语法 myloop n state = foldl process state <

我有一些类似以下的代码,它根据从磁盘读取的随机
样本
更新
状态

myloop 0 state = return state
myloop n state = do
  sample <- getRandomSampleFromFile
  myloop (n - 1) (process state sample)
myloop 0状态=返回状态
myloop n state=do
样本

您可以使用应用程序语法

myloop n state = foldl process state <$> mapM (const getRandomSampleFromFile) [1..n]
myloop n state=foldl进程状态mapM(const getRandomSampleFromFile)[1..n]
或(thk2@andrás-kovács)

myloop n state=foldl进程状态replicItem m getRandomSampleFromFile
如果您希望中断读取过程(或读取时处理数据),则必须输入monad

myloop n state = foldM acc state [1..n]
    where acc s _ | breakProcess s = return s
                  | otherwise      = process s <$> getRandomSampleFromFile
myloop n state=foldM acc state[1..n]
其中acc s 124; breakProcess s=返回s
|否则=进程的getRandomSampleFromFile
但是折叠不会停止,你最初的折叠(毕竟)看起来更好

myloop n state | breakProcess state = return state
               | otherwise          = do
                                        x <- getRandomSampleFromFile
                                        myloop (n - 1) (process state x)
myloop n状态| breakProcess状态=返回状态
|否则=做

这应该提供一个提示:

> import Control.Monad
> foldM (\n x -> print (n,x) >> return (n+x)) 0 [10,20,30]
(0,10)
(10,20)
(30,30)
60

在您的例子中,
n
是一个索引状态对(或者只有状态,如果计算中不需要索引),而
x
是手头的示例。

一旦您了解了它的工作原理,
foldr
在monad中工作时具有惊人的通用性:

myloop n = foldr w return [1..n] where
    w _ k state = do
        sample <- getRandomSampleFromFile
        k (process state sample)
所以

之所以这样做,是因为
foldr
的定义:

foldr f z [] = z
foldr f z (x:xn) = f x (foldr f z xn)

案例中,将递归调用放入thunk,tail调用
f
,并传递该thunk。您不必考虑这一点,因为在许多简单折叠中,
f
在其第二个参数中是严格的(因此递归调用在输入
f
主体之前有效地执行),但是
foldr
实际上立即控制
f
并让它决定何时(如果有的话)执行递归调用。因此,几乎任何递归结构都可以被重新编写为
foldr

我想提供我的解决方案,因为这已经让我绞尽脑汁一段时间了

我们需要的是具有以下签名的函数:

iteratively :: Monad m => (a -> m a) -> a -> [m a]
它应该认为,
迭代地mi
在先前动作的连续输出上重复应用一元动作
m
[1]。输出必须是一个一元动作数组的原因是,我们只对表示具有
n
连续应用程序的第
n
个一元动作感兴趣

我达成的实施方案如下:

iteratively step init = iterate (>>= step) (return init)
现在,使用initail value
init
重复了
n次的动作
m
是-因此类似于您的
myloop

repeatedly :: Monad m => (a -> m a) -> a -> Int -> m a
repeatedly step init n = iteratively step init !! n
[1] :
m
这里表示将参数返回到下一个操作的一元操作-您称之为
sample
。它可以通过
getRandomSampleFromFile
process
实现,因此:

process <$> getRandomSampleFromFile
处理getRandomSampleFromFile

我在原始问题中没有指出这一点,但这是否意味着我必须首先读取磁盘上的所有随机样本?这对于我的用例是不可行的。
mapM(const x)[1..n]=replictem nx
我不确定replictem是否必须读取内存中的所有样本。我可能错了。@mb14当然会的
ReplicatItem
sequence
traverse
mapM
只有在将列表视为数组的情况下才有意义。你也可以使用一个真正的数组类型,比如说
vector
,当你看到这些单词时,想想“可能是流媒体库?”。在长列表上,它们总是会累积列表并导致空间泄漏。这就是为什么我们有流媒体库。(例如,可以将
replicItem
&公司专门化为IO,然后使用类似惰性IO的东西,请参见)如果“
foldlM
作为
foldr
”,而不是“foldlM”`as
foldr
”,可以观察到空间泄漏。请尝试使用足够的文本行打印
main=myloop(+)(readLn::IO Int)1000000>=这可以通过使用
$解决
myloop n = foldr (\ _ k state -> getRandomSampleFromFile >>= k . process state) return [1..n]
foldr f z [] = z
foldr f z (x:xn) = f x (foldr f z xn)
iteratively :: Monad m => (a -> m a) -> a -> [m a]
iteratively step init = iterate (>>= step) (return init)
repeatedly :: Monad m => (a -> m a) -> a -> Int -> m a
repeatedly step init n = iteratively step init !! n
process <$> getRandomSampleFromFile