Haskell Lazy ByteString + 읽기 / 쓰기 진행 함수


Answers

우선, 상당수의 하스켈 프로그래머가 일반적으로 약간의 의혹으로 지연 IO를 고려해야한다. 기술적으로 순결을 위반하지만 일관된 입력으로 단일 프로그램을 실행할 때 (내가 아는 한) 눈에 띄지 않는 제한된 방식으로 [0] . 다른 한편으로는 많은 사람들이 그것을 매우 좋아합니다. 왜냐하면 매우 제한된 종류의 불순물 만 포함하기 때문입니다.

주문형 I / O를 사용하여 실제로 생성 된 지연 데이터 구조의 환상을 만들기 위해 readFile 과 같은 함수는 배경의 비속 한 속임을 사용하여 구현됩니다. 주문형 I / O를 짜는 것은 함수에 내재되어 있으므로, 일반 ByteString 을 가져 오는 착각이 납득할만한 이유와 같은 이유로 실제로 확장 할 수는 없습니다.

세부 정보를 손으로 잡고 의사 코드를 작성하면 readFile과 같은 기능은 기본적으로 다음과 같이 작동합니다.

lazyInput inp = lazyIO (lazyInput' inp)
lazyInput' inp = do x <- readFrom inp
                    if (endOfInput inp)
                        then return []
                        else do xs <- lazyInput inp
                                return (x:xs)

... lazyIO 가 호출 될 때마다 값이 실제로 사용될 때까지 I / O를 연기합니다. 실제 읽기가 발생할 때마다보고 기능을 호출하려면 직접 작성해야하며 일반화 된 버전의 기능을 작성할 수는 있지만 본인의 지식에는 존재하지 않습니다.

위의 것을 감안할 때 몇 가지 옵션이 있습니다.

  • 사용중인 Lazy I / O 함수의 구현을 찾아보고, 진행보고 기능을 포함하는 함수를 구현하십시오. 이것이 더러운 해킹처럼 느껴진다면 그것은 거의 그렇기 때문입니다. 그러나 거기에 있습니다.

  • 게으른 I / O를 포기하고보다 명확하고 구성 가능한 것으로 전환합니다. 이것은 하스켈 커뮤니티가 전체적으로 지향하는 방향으로, 구체적으로는 Iteratees의 변형을 향한 것으로 , 더 예측 가능한 동작을 갖는 멋지게 구성 가능한 작은 스트림 프로세서 빌딩 블록을 제공합니다. 단점은 개념이 여전히 적극적으로 개발되고 있으므로 구현에 대한 합의가 없거나이를 사용하는 법을 배우기위한 단일 출발점이 없다는 것입니다.

  • 게으른 I / O를 포기하고 일반 구형 I / O로 전환 : 청크를 읽고,보고 정보를 인쇄하며 최대한 많은 입력을 처리하는 IO 작업을 작성합니다. 완료 될 때까지 루프에서 호출하십시오. 입력에 대해 무엇을하는지, 처리 과정에서의 게으름에 얼마나 의존하는지에 따라 몇 가지 중요한 기능을 작성하는 것에서부터 많은 유한 상태 머신 스트림 프로세서를 구축하는 것, Iteratees를 재발 명하는 방법의 %.

[0] : 여기에 기본 함수는 unsafeInterleaveIO 라고 unsafeInterleaveIO , 최선의 방법은 불순물을 관찰 할 수있는 유일한 방법은 프로그램을 다른 입력으로 실행해야한다는 것입니다 (이 경우 다른 방식 으로든 동작 할 수 있습니다. 순수한 코드에서는 이해가되지 않는 방식으로), 또는 코드를 특정 방식으로 변경 (즉, 효과가 없어야하는 리팩토링은 비 로컬 효과를 가질 수 있습니다).

다음은 좀 더 구성 가능한 함수를 사용하여 "평범한 구식 I / O"방식으로 일하는 대략적인 예입니다.

import System
import System.IO
import qualified Data.ByteString.Lazy as B

main = do [from, to] <- getArgs
          -- withFile closes the handle for us after the action completes
          withFile from ReadMode $ \inH ->
            withFile to WriteMode $ \outH ->
                -- run the loop with the appropriate actions
                runloop (B.hGet inH 128) (processBytes outH) B.null

-- note the very generic type; this is useful, because it proves that the
-- runloop function can only execute what it's given, not do anything else
-- behind our backs.
runloop :: (Monad m) => m a -> (a -> m ()) -> (a -> Bool) -> m ()
runloop inp outp done = do x <- inp
                           if done x
                             then return ()
                             else do outp x
                                     runloop inp outp done

-- write the output and report progress to stdout. note that this can be easily
-- modified, or composed with other output functions.
processBytes :: Handle -> B.ByteString -> IO ()
processBytes h bs | B.null bs = return ()
                  | otherwise = do onReadBytes (fromIntegral $ B.length bs)
                                   B.hPut h bs

onReadBytes :: Integer -> IO ()
onReadBytes count = putStrLn $ "Bytes read: " ++ (show count)

"128"은 한 번에 읽을 바이트 수입니다. 내 "스택 오버플로 스 니펫"디렉토리에있는 임의의 소스 파일에서 실행 :

$ runhaskell ReadBStr.hs Corec.hs temp
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 83
$
Question

나는 Haskell Lazy IO를 사용하고있다.

나는 복사 진행을 콘솔로 출력하는 동안 큰 파일 (8Gb)을 복사하는 우아한 방법을 찾고있다.

파일을 자동으로 복사하는 다음의 간단한 프로그램을 생각해보십시오.

module Main where

import System
import qualified Data.ByteString.Lazy as B

main = do [from, to] <- getArgs
          body <- B.readFile from
          B.writeFile to body

거기에 당신이보고에 사용하려는 콜백 함수가 있다는 것을 상상해보십시오 :

onReadBytes :: Integer -> IO ()
onReadBytes count = putStrLn $ "Bytes read: " ++ (show count)

질문 : onReadBytes 함수를 Lazy ByteString 함수로 짜는 방법을 알려주므로 성공한 읽기에서 다시 호출 할 것인가? 또는이 디자인이 좋지 않다면, 하스켈은 그것을 어떻게 할 것입니까?

참고 : 콜백 빈도는 중요하지 않으며, 1,024 바이트마다 또는 1MB마다 호출 할 수 있습니다. 중요하지 않습니다.

답변 : camccann에게 많은 감사의 말을 전합니다. 나는 그것을 완전히 읽을 것을 제안한다.

Bellow는 camccann 코드를 기반으로하는 코드의 제 버전입니다. 유용 할 수 있습니다.

module Main where

import System
import System.IO
import qualified Data.ByteString.Lazy as B

main = do [from, to] <- getArgs
          withFile from ReadMode $ \fromH ->
            withFile to WriteMode $ \toH ->
              copyH fromH toH $ \x -> putStrLn $ "Bytes copied: " ++ show x

copyH :: Handle -> Handle -> (Integer -> IO()) -> IO ()
copyH fromH toH onProgress =
    copy (B.hGet fromH (256 * 1024)) (write toH) B.null onProgress
    where write o x  = do B.hPut o x
                          return . fromIntegral $ B.length x

copy :: (Monad m) => m a -> (a -> m Integer) -> (a -> Bool) -> (Integer -> m()) -> m()
copy = copy_ 0

copy_ :: (Monad m) => Integer -> m a -> (a -> m Integer) -> (a -> Bool) -> (Integer -> m()) -> m()
copy_ count inp outp done onProgress = do x <- inp
                                          unless (done x) $
                                            do n <- outp x
                                               onProgress (n + count)
                                               copy_ (n + count) inp outp done onProgress