两个Haskell程序如何通过stdin和stdout交换整数值而不将数据视为文本?

问题描述 投票:4回答:2

我有兴趣学习如何使用标准输入和输出在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,因此我想知道是否有一种简单的解决方案,不需要编写自定义二进制解析器(我不知道该怎么做)。谁能提供这种方法?

parsing haskell stdout stdin
2个回答
1
投票

这是我最后得到的代码:

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)

生成的程序可以通过管道传输到一起,并且生成的程序按要求运行。


-2
投票

我不确定序列化是否可以将您保存在这里,您仍然需要资源来进行序列化,反序列化。但是,如果您确实需要您写的内容,可以尝试以下操作: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),并且所需的内存也要少得多。至少在我的笔记本电脑上。如果有人可以解释我为什么我的答案不好,那将是很好的。是的,我知道您可以对实现类Bin​​ary的任何类型使用字节串+二进制序列化。但是这里我们是关于Int的。UPD 4。没有人打开github。所以我在这里添加了源代码。

© www.soinside.com 2019 - 2024. All rights reserved.