将抽象数据类型保存和加载到 json 文件并从游戏中的文件中读取 Haskell

Posted

技术标签:

【中文标题】将抽象数据类型保存和加载到 json 文件并从游戏中的文件中读取 Haskell【英文标题】:Saving and loading abstract data type to json file and reading from file in the game for Haskell 【发布时间】:2019-10-17 23:46:11 【问题描述】:

我正在用 Haskell 编写一个游戏,我想将抽象数据类型的列表保存到 JSON 格式的文本文件中,然后重新加载该保存的文件。即将该文件读回抽象数据类型列表,然后在我的游戏中正常使用该重新加载的列表。我对 Haskell 和一般编程很陌生,所以我有点不确定这是否可能首先。

我相信我已经正确设置了我的数据类型(房间)。但我不确定 FromJson 和 toJSON (语法正确吗?)。

另外,我目前保存的方式并没有以我想要的格式保存 JSON 文件:

["reward":"treasure","enemy":"enemyOne","description":"这是一个房间"]

而描述、敌人和奖励应该是顺序。

(我也相信数据类型 Room 应该在 JSON 文件中的所有内容之前正确吗?)。

如果您需要更多说明,请告诉我。谢谢!

-# LANGUAGE 
    OverloadedStrings
  , DeriveGeneric
 #-

module Game where

--import System.IO
import Text.Read
import Data.Char
import Prelude hiding (readFile, writeFile)

import Data.Aeson
import Data.Text as T hiding (length, tail)
import Data.ByteString.Lazy as B hiding (putStrLn, length, tail, writeFile, readFile)
import Data.ByteString.Lazy.Char8 as BC hiding (putStrLn, length, tail)
import GHC.Generics


data Room = Room
    description :: String
   , enemy :: String
   , reward :: String
    deriving (Show, Generic)

-- is this syntax correct for parseJSON?
instance FromJSON Room where
   parseJSON (Object v) =  Room <$> v .: "description" <*> v .: "enemy" <*> v .: "reward" 

-- is this syntax correct for toJSON? 
instance ToJSON Room where
    toJSON (Room desc enem reward) = object ["description" .= desc, "enemy" .= enem, "reward" .= reward]



save lst =
    do
    writeFile "savegame.txt" (encode lst)
    return()

load =
   do
   lst <- readFile "savegame.txt"
   let new = decode lst
   start new 

start :: [Room] -> IO()
start lst =
 putStrLn("Starting the game, need to use the lst as a list of rooms")

这些是我收到的错误消息:

for***.hs:46:14:
    No instance for (FromJSON a0) arising from a use of ‘decode’
    The type variable ‘a0’ is ambiguous
    Relevant bindings include
      new :: Maybe a0 (bound at for***.hs:46:8)
    Note: there are several potential instances:
      instance FromJSON DotNetTime
        -- Defined in ‘aeson-1.4.5.0:Data.Aeson.Types.FromJSON’
      instance FromJSON Value
        -- Defined in ‘aeson-1.4.5.0:Data.Aeson.Types.FromJSON’
      instance FromJSON a => FromJSON (Control.Applicative.Const a b)
        -- Defined in ‘aeson-1.4.5.0:Data.Aeson.Types.FromJSON’
      ...plus 89 others
    In the expression: decode lst
    In an equation for ‘new’: new = decode lst
    In the expression:
      do  lst <- readFile "savegame.txt";
           let new = decode lst;
           start new 

for***.hs:47:10:
    Couldn't match expected type ‘[Room]’ with actual type ‘Maybe a0’
    Relevant bindings include
      new :: Maybe a0 (bound at for***.hs:46:8)
    In the first argument of ‘start’, namely ‘new’
    In a stmt of a 'do' block: start new
Failed, modules loaded: none.
Prelude> 

【问题讨论】:

【参考方案1】:

Data.Aeson 中的函数decode,正如its documentation 所清楚的那样,返回的不是目标类型(无论您尝试解码的类型),而是包装在Maybe 中的目标类型。在您的情况下,这将是 Maybe [Room],而不仅仅是 [Room]

这反映了解码可能失败(即格式不正确或其他)的事实,在这种情况下,函数返回Nothing

这是编译器在说“相关绑定包括:new :: Maybe a0”时告诉你的 - 它说变量 new 已被推断为类型 Maybe a0 ,其中a0 是一些未知类型。

它接着告诉你它不能将Maybe a0 作为参数传递给start,因为start 需要[Room] 类型的参数:“无法匹配预期类型[Room] 实际类型为 'Maybe a0' ... 在 'start' 的第一个参数中"

要解决这个问题,您需要处理decode 返回Nothing 的可能性,如果它返回Just,则将其内容传递给start。像这样的:

load =
   do
   lst <- readFile "savegame.txt"
   let new = decode lst
   case new of
      Nothing -> 
         error "Incorrect file format"
      Just n ->
         start n

(请注意,调用error 不是处理意外情况的好方法;我只是将其用作示例)

【讨论】:

以上是关于将抽象数据类型保存和加载到 json 文件并从游戏中的文件中读取 Haskell的主要内容,如果未能解决你的问题,请参考以下文章

在 JSON 文件中保存和加载可为空的字符串

将对象的类类型保存到文件并在 C++ 中读回

将 JSON 保存到 NSUserDefaults 并从那里填充ableview 是真的吗?

加载和保存矢量到文件

将UIColor保存到NSUserDefaults并从NSUserDefaults加载

从 JSON 文件保存和加载表视图?