根据其维基的实现如下:
这是蓝图字符串格式的技术描述,用于与其他用户共享蓝图。
蓝图字符串是蓝图的json表示形式,它使用base 64进行编码,其版本字节位于前面(香草0.15中为0),然后使用zlib deflate进行压缩。因此,要从蓝图字符串中获取蓝图的json表示,请跳过第一个字节,对字符串进行base64解码,最后使用zlib deflate进行解压缩。
请记住,我对Haskell非常陌生,虽然我确实有一些函数式编程经验,但可能只是在我生命的早期才玩过它。
依赖项列表如下:
base> = 4.7 && <5
字节字符串
base64字节字符串
zlib
Main.hs
文件:module Main where
import Lib
import System.Environment
main :: IO ()
main = do
args <- getArgs
if null args
then putStrLn "Usage: factorio-exe <blueprint-string>"
else putStrLn (blueprintJson (head args))
Lib.hs
文件:module Lib
( blueprintJson
) where
import Codec.Compression.Zlib
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy.Char8 as L
import Data.ByteString.Base64
blueprintJson :: [Char] -> [Char]
blueprintJson str = bpDeflate (bpDecode str)
bpDecode :: [Char] -> C.ByteString
bpDecode str = let (Right decoded) = decode (C.pack (tail str)) in decoded
bpDeflate :: C.ByteString -> [Char]
bpDeflate bstr = L.unpack (decompress (L.fromStrict bstr))
要测试的示例蓝图字符串如下:
请注意,在解码时,可能会出现Windows命令提示符显示问题的字符。
#1 楼
老实说,没什么可说的。类型签名就在那里,这是一个加号。传达可能的错误并使函数总计
但是,
bpDecode
有两个可能的运行时错误源:tail
(部分错误)和Right _ = decode
,因为后者可以返回Left
。因此,bpDecode
也很偏。在Haskell中,我们尝试将部分功能保持在最低水平。 bpDecode
可能会失败,但是它的类型无法向我们传达。相反,
bpDecode
应该将Either String C.ByteString
返回为decode
。我们可以使用String
来传达另一个可能的错误,即一个空的String
:或者如果您不在乎空字符串的大小写,我们可以改用
drop 1
的tail
。即使我们的原始列表为空,前者也总是返回一个列表:bpDecode :: [Char] -> Either String C.ByteString
bpDecode "" = Left "bpDecode: Empty String"
bpDecode s = decode . C.pack . tail $ s
一些库提供了“我知道这是正确的”功能,您也可以:
bpDecode :: [Char] -> Either String C.ByteString
bpDecode = decode . C.pack . drop 1
但这是一个问题。但是,现在
bpDecode
总计了:对于任何输入,它都会产生输出。使用
hlint
查找可能的eta减少量虽然不是必需的,但这可能是个好时机了解有关
hlint
的信息。它将报告您代码的常见改进。在这种情况下,您可以将bpDeflate
写为bpDecode' :: [Char] -> C.ByteString
bpDecode' s = let (Right d) = bpDecode s in d
,将
blueprintJson
写为bpDeflate :: C.ByteString -> [Char]
bpDeflate = L.unpack . decompress . L.fromStrict
单个字符流类型
Haskell有5种类型适用于字符串。
ByteString
(延迟和严格),Text
(延迟和严格)和String
(又称为[Char]
)。如果可能,请尝试坚持使用其中一种,不要一直在它们之间切换。例如,您的所有函数都可以用惰性ByteString
编写:blueprintJson :: [Char] -> [Char]
blueprintJson = bpDeflate . bpDecode
请注意,我更改了
blueprintJson
的类型,因为我们也更改了bpDecode
的类型。如果您不熟悉fmap
:在这种情况下,您可以将fmap
视为import Codec.Compression.Zlib
import qualified Data.ByteString.Lazy.Char8 as L
import Data.ByteString.Base64.Lazy
blueprintJson :: L.ByteString -> Either String L.ByteString
blueprintJson = fmap bpDeflate . bpDecode
bpDecode :: L.ByteString -> Either String L.ByteString
bpDecode = decode . L.drop 1
bpDeflate :: L.ByteString -> L.ByteString
bpDeflate = decompress
无论如何,字符串类型当然取决于您以后的使用情况。由于
blueprintJson
暗示您将解码JSON,因此您稍后可能会使用aeson,后者也会使用惰性ByteString
。鉴于上述功能,我们现在可以将
blueprintJson
编写为fmap f (Right r) = Right (f r)
fmap _ (Left l) = Left l
首选模式匹配而不是布尔值保护。
在
main
中,检查列表是否为null
。这仍然使我们能够接受blueprintJson :: L.ByteString -> Either String L.ByteString
blueprintJson = fmap decompress . decode . L.drop 1
blueprintJson' :: L.ByteString -> L.ByteString
blueprintJson' = fromRight . blueprintJson
fromRight :: Either a b -> b
fromRight e = -- left as an exercise
如果您进行模式匹配,就不能在空白列表上意外使用
head
:main :: IO ()
main = do
args <- getArgs
if null args
then putStrLn (blueprintJson (head args)) -- woops!
else putStrLn "Usage: factorio-exe <blueprint-string>"
评论
\ $ \ begingroup \ $
将惰性ByteString用于解码功能将如何工作?在hackage.haskell.org/package/base64-bytestring-1.0.0.1/docs/…上,它声明使用严格的变体,所以我不知道它如何工作。
\ $ \ endgroup \ $
–skiwi
18 Mar 25 '18在15:48
\ $ \ begingroup \ $
@skiwi也有一个懒惰的变体:hackage.haskell.org/package/base64-bytestring-1.0.0.1/docs/…。我不小心忘记了更改导入。
\ $ \ endgroup \ $
– Zeta
18-3-25在16:58