代码之家  ›  专栏  ›  技术社区  ›  Shersh

解码和来自aeson软件包的解码功能之间有什么区别?

  •  16
  • Shersh  · 技术社区  · 7 年前

    功能 decode decode' aeson 包装几乎完全相同。但它们在文档中有细微的区别(只在此处发布文档中感兴趣的部分):

    -- This function parses immediately, but defers conversion.  See
    -- 'json' for details.
    decode :: (FromJSON a) => L.ByteString -> Maybe a
    decode = decodeWith jsonEOF fromJSON
    
    -- This function parses and performs conversion immediately.  See
    -- 'json'' for details.
    decode' :: (FromJSON a) => L.ByteString -> Maybe a
    decode' = decodeWith jsonEOF' fromJSON
    

    我试着去读 json json' 函数,但仍然不知道应该使用哪一个以及何时使用,因为文档不够清楚。有谁能更准确地描述两个函数之间的差异,并在可能的情况下提供一些行为解释的示例吗?

    更新:

    decodeStrict decodeStrict' 解码' 例如,顺便提一下,这也是一个有趣的问题。但是,在所有这些函数中,什么是懒惰的,什么是严格的,这一点都不明显。

    2 回复  |  直到 7 年前
        1
  •  15
  •   Alexis King    7 年前

    这两者之间的区别很微妙。那里

    这个 Value 类型

    Value 以及它的内部表示。这立即意味着 Bool (当然, Null 必须 每年进行一次全面评估 价值 根据WHNF进行评估。

    接下来,让我们考虑一下 String Number 这个 严格的 Text 所以那里也不会有懒惰。类似地 数字 Scientific 值,内部由两个严格值表示。二者都 一串 数字 而且 每年进行一次全面评估 根据WHNF进行评估。

    我们现在可以把注意力转向 Object Array 对象 在aeson中,s由a表示 懒惰的 HashMap 懒惰的 大堆 s是 Vector 价值 s可以包含thunk。

    考虑到这一点,我们知道,一旦我们有了 价值 只有 放置 decode decode' 不同之处在于对象和数组的生成。

    接下来,我们可以尝试实际评估GHCi中的一些内容,看看会发生什么。我们从一系列导入和定义开始:

    :seti -XOverloadedStrings
    
    import Control.Exception
    import Control.Monad
    import Data.Aeson
    import Data.ByteString.Lazy (ByteString)
    import Data.List (foldl')
    import qualified Data.HashMap.Lazy as M
    import qualified Data.Vector as V
    
    :{
    forceSpine :: [a] -> IO ()
    forceSpine = evaluate . foldl' const ()
    :}
    

    let jsonDocument = "{ \"value\": [1, { \"value\": [2, 3] }] }" :: ByteString
    
    let !parsed = decode jsonDocument :: Maybe Value
    let !parsed' = decode' jsonDocument :: Maybe Value
    force parsed
    force parsed'
    

    现在我们有两个绑定, parsed parsed' ,其中一个用 解码 。他们被强迫使用WHNF,这样我们至少可以看到他们是什么,但我们可以使用 :sprint

    ghci> :sprint parsed
    parsed = Just _
    ghci> :sprint parsed'
    parsed' = Just
                (Object
                   (unordered-containers-0.2.8.0:Data.HashMap.Base.Leaf
                      15939318180211476069 (Data.Text.Internal.Text _ 0 5)
                      (Array (Data.Vector.Vector 0 2 _))))
    

    仍然未评估,但使用 解码' 有一些数据。这让我们看到了两者之间第一个有意义的区别: 将其直接结果强制给WHNF,但是 将其推迟到需要时。

    让我们看看这些值,看看我们是否能找到更多的差异。一旦我们评估这些外部物体,会发生什么?

    let (Just outerObjValue) = parsed
    let (Just outerObjValue') = parsed'
    force outerObjValue
    force outerObjValue'
    
    ghci> :sprint outerObjValue
    outerObjValue = Object
                      (unordered-containers-0.2.8.0:Data.HashMap.Base.Leaf
                         15939318180211476069 (Data.Text.Internal.Text _ 0 5)
                         (Array (Data.Vector.Vector 0 2 _)))
    
    ghci> :sprint outerObjValue'
    outerObjValue' = Object
                       (unordered-containers-0.2.8.0:Data.HashMap.Base.Leaf
                          15939318180211476069 (Data.Text.Internal.Text _ 0 5)
                          (Array (Data.Vector.Vector 0 2 _)))
    

    这很明显。我们显式地强制这两个对象,因此现在它们都被计算为哈希映射。真正的问题是他们的 元素 进行评估。

    let (Array outerArr) = outerObj M.! "value"
    let (Array outerArr') = outerObj' M.! "value"
    let outerArrLst = V.toList outerArr
    let outerArrLst' = V.toList outerArr'
    
    forceSpine outerArrLst
    forceSpine outerArrLst'
    
    ghci> :sprint outerArrLst
    outerArrLst = [_,_]
    
    ghci> :sprint outerArrLst'
    outerArrLst' = [Number (Data.Scientific.Scientific 1 0),
                    Object
                      (unordered-containers-0.2.8.0:Data.HashMap.Base.Leaf
                         15939318180211476069 (Data.Text.Internal.Text _ 0 5)
                         (Array (Data.Vector.Vector 0 2 _)))]
    

    解码 ,值不是强制的,而是用 是正如你所见,这意味着 解码 在实际需要Haskell值之前,不会对其进行转换,这就是文档所说的延迟转换的意思。

    影响

    显然,这两个功能是 轻微地 不同,而且很明显, 解码' 比严格 解码

    嗯,值得一提的是 解码 解码' 解码 可能是正确的默认值。当然 永远不会比 解码 避免分配

    当然,懒惰也不是免费的。懒惰意味着添加thunk,这可能需要花费空间和时间。无论如何,如果要评估所有Thunk,那么 解码

    从这个意义上讲,您可能希望使用 解码' 在这种情况下 无论如何,结构将被强迫,这可能取决于 FromJSON 您正在使用的实例。一般来说,除非性能真的很重要,否则我不会担心在它们之间进行选择 解码'

        2
  •  1
  •   Fyodor Soikin    7 年前

    哈斯克尔是一种懒惰的语言。当你调用一个函数时,它实际上并没有立即执行,相反,关于调用的信息被“记住”并返回到堆栈中(这个记住的调用信息在文档中被称为“thunk”),只有当堆栈中的某个人真的厌倦了用返回的值做某事时,才会发生实际调用。

    json decode 工作但是有一种方法可以“欺骗”懒惰,并告诉编译器立即执行代码和计算值。这就是 json' decode'

    解码 节省计算时间,以防您实际上从未对该值执行任何操作,而 省去了“记住”通话信息(“thunk”)的必要性,但代价是执行所有操作。