我有兴趣学习如何使用标准输入和输出在Haskell程序之间高效地发送数据。假设我想将两个程序一起管道传输:“ P1”将数字5输出到stdout,“ P2”从stdin中获取一个整数,加1,然后再次将其输出到stdout。现在,我所知道的最好方法是将数据从P1输出为文本,将其解析回P2中的整数,然后从那里进行处理。例如:
P1.hs:
module Main where
main = do
print 5
P2.hs:
module Main where
main = fmap manipulateData getLine >>= print
where
manipulateData = (+ 1) . (read :: String -> Int)
输出:
$ (stack exec p1) | (stack exec p2)
6
如果可能,我想使用标准的I / O发送整数而不将其视为文本。我假设这仍然需要某种解析才能工作,但是我希望可以将数据解析为二进制并获得更快的程序。
Haskell有什么方法可以使这一过程变得简单吗?由于我要从一种基本的Haskell数据类型(Int)再次转到同一类型,并在中间通过标准I / O,因此我想知道是否有一种简单的解决方案,不需要编写自定义二进制解析器(我不知道该怎么做)。谁能提供这种方法?
这是我最后得到的代码:
module Main where
import qualified Data.ByteString.Lazy as BS
import qualified Data.Binary as B
main :: IO ()
main = do
dat <- BS.getContents
print $ (B.decode dat :: Int) + 1
另一个程序使用类似的导入和输出5,并带有以下行:
BS.putStr $ B.encode (5 :: Int)
生成的程序可以通过管道传输到一起,并且生成的程序按要求运行。
我不确定序列化是否可以将您保存在这里,您仍然需要资源来进行序列化,反序列化。但是,如果您确实需要您写的内容,可以尝试以下操作:Lib.hs
module Lib
( fastPipe
, fastUnpipe
) where
import System.IO
import Unsafe.Coerce
fastPipe :: Handle -> Int -> IO ()
fastPipe h x | x >= 0 = do hPutChar h '+'
fastPipe' h x
| otherwise = do hPutChar h '-'
fastPipe' h ((-1) - x)
fastPipe' :: Handle -> Int -> IO ()
fastPipe' h x = do
let (d, m) = x `divMod` 58
(hPutChar h . (unsafeCoerce :: Int -> Char) . (+ 65)) m
if d > 0
then fastPipe' h d
else hPutChar h '$'
fastUnpipe :: Handle -> IO Int
fastUnpipe h = do
c <- hGetChar h
x <- fastUnpipe' h 0 1
(return . (if c == '-' then ((-1) -) else id)) x
-- I want use tail recursion, so I need one extra parameter here
fastUnpipe' :: Handle -> Int -> Int -> IO Int
fastUnpipe' h x m = do
c <- hGetChar h
if c == '$'
then return x
else fastUnpipe' h (x + ((\y -> (y - 65) * m) . (unsafeCoerce :: Char -> Int)) c) (58 * m)
p1.hs
module Main where
import Lib (fastPipe)
import System.IO (stdout)
main :: IO ()
main = fastPipe stdout 45845948954 -- (minBound :: Int) (maxBound :: Int) or whatever you want
p2.hs
module Main where
import Lib (fastUnpipe)
import System.IO (stdin)
main :: IO ()
main = (+5) <$> fastUnpipe stdin >>= print
不要忘记,它对Integer无效,因为Integer的内部表示形式与Int或Char完全不同。也许最好不要这样。
UPD 3。我仍然不确定我是否理解我的答案被否决的原因。 unsafeCoerce并不是一件好事,但有时它可以比其他方法更快地工作,只是因为它不需要任何操作。在这里,我试图解释我的想法:https://github.com/DKurilo/serialization-test您可以看到,在这里使用unsafeCoerce进行序列化已经足够安全,可以使其工作得更快一些(请检查test.sh),并且所需的内存也要少得多。至少在我的笔记本电脑上。如果有人可以解释我为什么我的答案不好,那将是很好的。是的,我知道您可以对实现类Binary的任何类型使用字节串+二进制序列化。但是这里我们是关于Int的。UPD 4。没有人打开github。所以我在这里添加了源代码。