Conduit — объединение нескольких источников/производителей в один

Я читаю из файла с помощью sourceFile, но мне также нужно ввести случайность в операцию обработки. Я считаю, что лучший подход — это иметь продюсера типа

Producer m (StdGen, ByteString)

где StdGen используется для генерации случайного числа.

Я намереваюсь, чтобы производитель выполнял задачу исходного файла, а также создавал новое семя для получения урожая каждый раз, когда он отправляет данные вниз по течению.

Моя проблема в том, что, похоже, нет такого объединителя источников, как zipSink для приемников. Чтение обзора канала кажется, что вы можете встроить Source внутрь Conduit, но я не вижу, как это делается в примере.

Может ли кто-нибудь привести пример, в котором вы объединяете два или более источника ввода-вывода в один единственный Producer/Source?

ИЗМЕНИТЬ:

Пример:

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}

import System.Random (StdGen(..), split, newStdGen, randomR)
import ClassyPrelude.Conduit as Prelude
import Control.Monad.Trans.Resource (runResourceT, ResourceT(..))
import qualified Data.ByteString as BS

-- generate a infinite source of random number seeds
sourceStdGen :: MonadIO m => Source m StdGen
sourceStdGen = do
    g <- liftIO newStdGen
    loop g
    where loop gin = do
            let g' = fst (split gin)
            yield gin
            loop g'

-- combine the sources into one
sourceInput :: (MonadResource m, MonadIO m) => FilePath -> Source m (StdGen, ByteString)
sourceInput fp = getZipSource $ (,)
    <$> ZipSource sourceStdGen
    <*> ZipSource (sourceFile fp)

-- a simple conduit, which generates a random number from provide StdGen
-- and append the byte value to the provided ByteString
simpleConduit :: Conduit (StdGen, ByteString) (ResourceT IO) ByteString
simpleConduit = mapC process 

process :: (StdGen, ByteString) -> ByteString
process (g, bs) =
    let rnd = fst $ randomR (40,50) g
    in bs ++ pack [rnd]

main :: IO ()
main = do
    runResourceT $ sourceInput "test.txt" $$ simpleConduit =$ sinkFile "output.txt"

Таким образом, этот пример берет то, что находится во входном файле, и записывает его в выходной файл, а также добавляет случайное значение ASCII между 40 и 50 в конец файла. (Не спрашивайте меня, почему)


person Jacob Wang    schedule 27.04.2014    source источник
comment
Если вам нужна случайность в канале, используйте монаду random.   -  person PyRulez    schedule 28.06.2015


Ответы (2)


Для этого вы можете использовать ZipSource. В вашем случае это может выглядеть примерно так:

sourceStdGens :: Source m StdGen
sourceBytes :: Source m ByteString
sourceBoth :: Source m (StdGen, ByteString)
sourceBoth = getZipSource $ (,)
    <$> ZipSource sourceStdGens
    <*> ZipSource sourceBytes
person Michael Snoyman    schedule 27.04.2014
comment
Или, если вам просто нужно заархивировать 2 источника, Data.Conduit.Internal.zipSources, но тип ZipSource обобщает эту функцию, добавляя экземпляр Applicative для выходного параметра. - person bheklilr; 27.04.2014

Вы можете сделать это в монаде IO, а затем перенести результат в Producer.

do (i, newSeed) <- next currentSeed
   b <- generateByteStringFromRandomNumber i
   return (b, newSeed)

Это действие ввода-вывода можно поднять в соответствующий канал с помощью простого лифта:

-- assuming the above action is named x and takes the current seed as an argument
-- the corresponding producer/source is:
lift $ x currentSeed
person cassandracomar    schedule 27.04.2014
comment
Не могли бы вы привести пример, в котором вы «поднимаете» действие ввода-вывода до Producer? - person Jacob Wang; 28.04.2014