【问题标题】:Haskell sequence of IO actions processing with filtration their results in realtime+perfoming some IO actions in certain momentsHaskell IO 动作序列处理,实时过滤它们的结果+在某些时刻执行一些 IO 动作
【发布时间】:2025-11-28 04:15:01
【问题描述】:

我想做一些无限序列的 IO 动作处理,实时过滤它们的结果 + 在某些时刻执行一些 IO 动作: 我们有一些减少序列的功能(请参阅我的问题haskell elegant way to filter (reduce) sequences of duplicates from infinte list of numbers):

f :: Eq a => [a] -> [a]
f = map head . group

和表达

join $ sequence <$> ((\l -> (print <$> l)) <$> (f <$> (sequence $ replicate 6 getLine)))

如果我们运行它,用户可以生成任何数字序列,例如:

1
2
2
3
3
"1"
"2"
"3"
[(),(),()]

这意味着首先执行了所有 getLine 操作(示例中执行了 6 次,最后执行了过滤列表的所有 IO 操作,但我想准确地执行 IO 操作,然后对某些子序列进行排序减少完成相同的数字。

如何归档此输出:

1     
2
"1"
2     
3
"2"        
3
3
"3"
[(),(),()]

所以我希望这个表达式不要挂起:

 join $ sequence <$> ((\l -> (print <$> l)) <$> (f <$> (sequence $ repeat getLine)))

如何在不阻塞无限列表的情况下按上述方式存档实时输出?

【问题讨论】:

  • 请描述您想要生成的内容,而不是假设通过查看现有代码很明显。
  • 您是否假设输入是递增顺序,并且您希望在输入下一个更大的数字时输出每个唯一数字?
  • 不,可以是任意数字的序列,相同数字的重复子序列必须减少为一个数字。
  • 那么究竟是什么让你决定输出,例如,"1" 当你这样做的时候?是什么让你等到输入 2 之后,如果 1 再次出现在输出中会发生什么?输出似乎没有尽可能早或尽可能晚地产生。
  • @chepner 如果下一个输入与前一个不同,这意味着 seq 已“损坏”,我们可以执行前一个数字的打印操作。

标签: haskell io sequence


【解决方案1】:

我用 iterateUntilM 找到了一个很好的解决方案

iterateUntilM (\_->False) (\pn -> getLine >>= (\n -> if n==pn then return n else (if pn/="" then print pn else return ()) >> return n) ) ""

我不喜欢冗长的

(if pn/="" then print pn else return ())

如果您知道如何减少这种情况,请发表评论)

ps。 值得注意的是,我对这个功能做了一个video :) 并且无法立即应用它:(

【讨论】:

    【解决方案2】:

    这似乎是流媒体库的工作,例如streaming

    {-# LANGUAGE ImportQualifiedPost #-}
    module Main where
    
    import Streaming
    import Streaming.Prelude qualified as S
    
    main :: IO ()
    main =
          S.mapM_ print
        . S.catMaybes
        . S.mapped S.head
        . S.group
        $ S.replicateM 6 getLine
    

    “流”有一个类似于列表的 API,但适用于有效的序列。

    group 的流式传输版本的好处在于,如果不需要,它不会强迫您将整个组保存在内存中。


    这个答案中最不直观的函数是mapped,因为它非常通用。流的head 版本是否适合作为其参数并不明显。关键思想是Stream 类型可以表示both 正常有效序列,以及在其上划分组的元素序列。这是通过更改函子类型参数来控制的(在第一种情况下是Of,在grouped Streams 情况下是嵌套的Stream (Of a) m)。

    mapped 让我们在对底层 monad 产生一些影响的同时转换该函子参数(此处为 IO)。 head 处理内部的 Stream (Of a) m 组,让我们回到 Of (Maybe a) 函子参数。

    【讨论】:

      【解决方案3】:

      如果没有第三方库,您可以懒惰地读取标准输入的内容,在预期输入的末尾附加一个虚拟字符串以强制输出。 (我愚蠢地忽略了可能有更好的解决方案。)

      import System.IO
      
      print_unique :: (String, String) -> IO ()
      print_unique (last, current) | last == current = return ()
                                   | otherwise = print last
      
      main = do
        contents <- take 6 <$> lines <$> hGetContents stdin
        traverse print_unique (zip <*> tail $ (contents ++ [""]))
      

      zip &lt;*&gt; tail 生成由ith 和i+1st 行组成的元组,没有阻塞。 print_unique 然后如果下一行不同就立即输出一行。

      本质上,您是在执行输入时对输出操作进行排序,而不是对输入操作进行排序。

      【讨论】:

        最近更新 更多