Я хочу сделать какую-то бесконечную последовательность обработки IO-действий с фильтрацией их результатов в реальном времени + выполнение некоторых IO-действий в определенные моменты: У нас есть некоторая функция для сокращения последовательностей (см. мой вопрос элегантный способ haskell для фильтрации (уменьшения) последовательностей дубликатов из бесконечного списка чисел):
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 раз, а в конце выполняются все действия ввода-вывода для отфильтрованного списка, но я хочу выполнять действия ввода-вывода именно в моменты, когда последовательность сокращается для некоторых подпоследовательностей одинаковых чисел .
Как я могу заархивировать этот вывод:
1
2
"1"
2
3
"2"
3
3
"3"
[(),(),()]
Итак, я хочу, чтобы это выражение не зависало:
join $ sequence <$> ((\l -> (print <$> l)) <$> (f <$> (sequence $ repeat getLine)))
Как я могу архивировать вывод в реальном времени, как описано выше, не блокируя его в бесконечных списках?
Вы предполагаете, что ввод осуществляется в порядке возрастания, и хотите выводить каждое уникальное число, когда вводится следующее большее число?
Нет, это может быть последовательность любых чисел, повторяющиеся подпоследовательности одних и тех же чисел должны быть сведены к одному числу.
Итак, что именно заставляет вас выводить, например, "1"
, когда вы это делаете? Что заставило вас ждать, пока не будет введено 2, и что произойдет, если 1 снова появится в выводе позже? Выходные данные, похоже, не производятся ни как можно раньше, ни как можно позже.
@chepner, если следующий ввод отличается от предыдущего, это означает, что seq «сломан», и мы можем выполнить действие печати предыдущего числа.
Ах, хорошо, дублируйте подпоследовательности. Я упустил это различие.
Это похоже на работу для потоковой библиотеки, например, потоковой передачи.
{-# 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 заключается в том, что она не заставляет вас хранить всю группу в памяти, если она не нужна.
Наименее интуитивная функция в этом ответе — отображается , потому что она очень общая. Не очевидно, что потоковая версия head подходит в качестве его параметра. Ключевая идея заключается в том, что тип Stream может представлять как обычные эффективные последовательности, так и последовательности элементов, на которых разграничены группы. Это управляется изменением параметра типа функтора ( Of в первом случае, вложенного Stream (Of a) m
в случае сгруппированных потоков).
mapped
давайте вы преобразуете этот параметр функтора, оказав некоторое влияние на базовую монаду (здесь IO
). head
обрабатывает внутренние Stream (Of a) m
группы, возвращая нас к параметру Of (Maybe a)
функтора.
Без сторонней библиотеки вы можете лениво читать содержимое стандартного ввода, добавляя фиктивную строку в конец ожидаемого ввода для принудительного вывода. (Вероятно, есть лучшее решение, которое я тупо упускаю из виду.)
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 <*> tail
производит кортежи, состоящие из i
-й и i+1
-й строк без блокировки. print_unique
затем немедленно выводит строку, если следующая строка отличается.
По сути, вы упорядочиваете действия вывода по мере выполнения ввода, а не действия ввода.
Я нашел хорошее решение с 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 ())
если вы знаете, как уменьшить это, пожалуйста, напишите)
пс. Примечательно, что я снял видео об этой функции :) И не смогла сразу применить :(
Пожалуйста, опишите, что вы хотите сгенерировать, а не предполагайте, что это очевидно, глядя на ваш существующий код.