Декодирование Factorio чертежи в Haskell


В игре Factorio есть строка в кодировке представления чертежей, которые я хотел расшифровать.

Осуществление в соответствии с их Вики следующее:

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

Строки плана является JSON-схему, закодированная при помощи 64 с байтом версии перед (0 в ванили 0.15), а затем сжатого при помощи zlib сдуваться. Так, чтобы получить JSON-представление под копирку из строки копирку, пропустить первый байт в base64 декодировать строку, и, наконец, распаковать с помощью zlib сдуваться.

Пожалуйста, имейте в виду, что я очень новичок в Хаскелл, отыграв только с ним возможно раньше, как только в моей жизни, хотя у меня есть некоторый опыт программирования.

Список зависимостей состоит в следующем:

  • базы >= 4.7 && < 5
  • bytestring
  • в base64-bytestring
  • библиотека 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))

Пример строки копирку для тестирования состоит в следующем:

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

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



1127
22
задан 24 марта 2018 в 09:03 Источник Поделиться
Комментарии
1 ответ

Там не так много, чтобы сказать, чтобы быть честным. Подписи типа все есть, что является плюсом.

Возможные сообщения об ошибках и делают функции общая

Однако bpDecode есть две возможные ошибки во время выполнения Источники: tail (что частично) Right _ = decode, поскольку последний может вернуть Left. Поэтому bpDecode частично тоже. В Haskell, мы стараемся сохранить частичные функции к минимуму. bpDecode может потерпеть неудачу, но это типа не общаться с нами.

Вместо этого bpDecode должны вернуть Either String C.ByteString как decode. Мы можем использовать String для связи другая возможная ошибка, а именно пустой String:

bpDecode :: [Char] -> Either String C.ByteString
bpDecode "" = Left "bpDecode: Empty String"
bpDecode s = decode . C.pack . tail $ s

Кроме того, если вы не заботитесь о пустую строку случае, мы можем использовать drop 1 вместо tail. Бывший всегда возвращает список, даже если наш исходный список пуст:

bpDecode :: [Char] -> Either String C.ByteString
bpDecode = decode . C.pack . drop 1

Некоторые библиотеки предоставляют "я знаю, что это правильная функция" и Вы тоже можете:

bpDecode' :: [Char] -> C.ByteString
bpDecode' s = let (Right d) = bpDecode s in d

но это в сторону. Однако, теперь bpDecode общее: для любой входной сигнал будет выдавать выходной.

Использовать hlint чтобы найти возможные ета-сокращение

Пока не надо, это может быть хорошее время, чтобы узнать о hlint. Он сообщит общие улучшения кода. В этом случае, вы можете написать bpDeflate как

bpDeflate :: C.ByteString -> [Char]
bpDeflate = L.unpack . decompress . L.fromStrict

и blueprintJson как

blueprintJson :: [Char] -> [Char]
blueprintJson = bpDeflate . bpDecode

Постарайтесь оставаться в один символ типа потока

Хаскелл имеет 5 типов, которые работают со строками. ByteString (ленивый и строгий), Text (ленивый и строгий) и String (ака [Char]). Если возможно, старайтесь придерживаться одной из них и не переключаться между ними все время. Например, все ваши функции может быть записан с ленивым ByteStringС:

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'ы типа, поскольку мы также изменили bpDecode'ы тип. В случае, если вы не знакомы с fmapв этом контексте можно подумать fmap как

fmap f (Right r) = Right (f r)
fmap _ (Left l) = Left l

Независимо от того, строкового типа зависит, конечно, в вашем случае использовать. Так blueprintJson намекает, что вы декодировать JSON, то вы, вероятно, использовать aeson позже, которые использует ленивый ByteStringS Как хорошо.

Учитывая эти функции, мы можем теперь написать blueprintJson как

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

Предпочитаете картины играм, а не логического гв

В mainвы проверьте, будет ли список null. Что еще позволяет нам нелепой слу

main :: IO ()
main = do
args <- getArgs
if null args
then putStrLn (blueprintJson (head args)) -- woops!
else putStrLn "Usage: factorio-exe <blueprint-string>"

Если вы шаблон матч нельзя случайно использовать head на пустой список:

main :: IO ()
main = do
args <- getArgs
case args of
[b] -> putStrLn (blueprintJson b)
_ -> putStrLn "Usage: factorio-exe <blueprint-string>"

17
ответ дан 24 марта 2018 в 09:03 Источник Поделиться