gpt4 book ai didi

haskell - 在 Haskell 中使用 IO 模仿 Python 生成器

转载 作者:行者123 更新时间:2023-12-03 14:27:06 24 4
gpt4 key购买 nike

这是一个关于如何在 Haskell 中做某事的问题,这在 Python3 中很容易做到。

我有一个使用生成器的 Python3 程序,如下所示:

def gen(filename):
for line in open(filename):
line = line.rstrip()
print(f"From {filename} about to yield the line {line}")
yield line
print(f"From {filename} recently yielded the line {line}")
if "." in line:
yield from gen(line)

for line in gen("a.txt"):
print(f"Main program has {line}")

如果我给它一个包含以下内容的输入文件 a.txt:
First line of a
b.txt
Third line of a

另一个输入文件 b.txt 包含以下内容:
First line of b
Second line of b

那么输出,正如我所料,是:
From a.txt about to yield the line First line of a
Main program has First line of a
From a.txt recently yielded the line First line of a
From a.txt about to yield the line b.txt
Main program has b.txt
From a.txt recently yielded the line b.txt
From b.txt about to yield the line First line of b
Main program has First line of b
From b.txt recently yielded the line First line of b
From b.txt about to yield the line Second line of b
Main program has Second line of b
From b.txt recently yielded the line Second line of b
From a.txt about to yield the line Third line of a
Main program has Third line of a
From a.txt recently yielded the line Third line of a

我非常希望能够在 Haskell 中做同样的事情。我需要保留 Python 版本的基本结构,将 gen() 保持为一个函数(或半协程,如果你想调用它),我可以从不同的地方调用它。

在我尝试用 Haskell 编写它时,我遇到了诸如以下类型的可怕困惑:
IO [IO String]

我怀疑我的做法是错误的。

有什么建议?

最佳答案

您要查找的数据类型是 FreeT :

data FreeF f a b = Pure a | Free (f b)
newtype FreeT f m a = FreeT { runFreeT :: m (FreeF f a (FreeT f m a)) }
FreeT f m a表示“ m 后跟 f 的交替层,除了在任何一点,而不是 f -层,可以有一个终止 a -value”。等同于您的生成器的这种类型的特定形式是
type Generator a = FreeT ((,) a) IO ()

A Generator aIO 的交替层-计算和 a -生产,除了 IO 之一-computations 可以通过产生 () 来终止生成反而。
instance (Functor f, Monad m) => Monad (FreeT f m)
instance MonadTrans (FreeT f)
lift :: (MonadTrans t, Monad m) => m a -> t m a
lift :: Monad m => m a -> FreeT f m a
liftF :: (Functor f, MonadFree f m) => f a -> m a
liftF :: (Functor f, Monad m) => f a -> FreeT f m a

所以你可以写 gen以这种方式:
-- singleton generator
yield :: a -> Generator a
yield x = liftF (x, ())
-- empty generator (unused/part of when)
end :: Generator a
end = return ()
gen :: FilePath -> Generator String
gen path = do handle <- lift $ openFile path
fileLines <- lift $ lines <$> hGetContents handle
forM_ fileLines $ \line -> do
lift $ putStrLn $ "From " ++ path ++ " about to yield the line " ++ line
yield line
lift $ putStrLn $ "From " ++ path ++ " recently yielded the line " ++ line
when ('.' `elem` line) $ gen line
lift $ hClose handle

然后你可以拆掉这个结构:
main = iterT (\(line, continue) -> putStrLn ("Main program has " ++ line) >> continue)
(gen "a.txt")
FreeT有一个堂兄,名叫 Stream .这几乎是一样的东西,只是它使用了一些技巧来挤出更多的性能。 (代价是现在的相等性比以前更模糊,但无论如何我们通常都不关心控制结构的相等性。)具体来说,不是交替 mf层, Stream只是 m 的序列和 f以任何顺序创建图层。只要 Monad m ,这很好,因为相邻 m层可以总是 join和新一起编 pure相邻 f 之间形成的-层,直到 FreeT的交替结构被恢复。

如果您使用 Stream (您可能应该这样做),那么您可能想要替换 (,) Of ,来自同一个包:
data Of a b = !a :> b
type Generator a = Stream (Of a) IO ()

严格性对 String 不是特别有用,但它确实读起来更好。它对其他类型更有用,例如 Int .

关于haskell - 在 Haskell 中使用 IO 模仿 Python 生成器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54066139/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com