gpt4 book ai didi

json - aeson 包中的解码和解码功能有什么区别?

转载 作者:行者123 更新时间:2023-12-02 01:34:39 25 4
gpt4 key购买 nike

函数 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' 职能。我不是问 decode' 之间有什么区别和 decodeStrict例如,顺便说一句,这也是一个有趣的问题。但是在所有这些函数中,什么是惰性的,什么是严格的,则根本不明显。

最佳答案

这两者之间的区别是微妙的。有区别,但有点复杂。我们可以从查看类型开始。
Value类型

重要的是要注意 Value aeson 提供的 type 已经严格了很长时间(特别是从 0.4.0.0 版本开始)。这意味着在 Value 的构造函数之间不能有任何 thunk。及其内部表示。这立即意味着 Bool (当然, Null )必须在一次 Value 被完全评估评估为 WHNF。

接下来,让我们考虑StringNumber . String构造函数包含一个 类型的值严格 Text ,所以那里也不能有任何懒惰。同样,Number构造函数包含一个 Scientific值,内部由两个严格值表示。两者 StringNumber还必须完全评估一次 Value评估为 WHNF。

我们现在可以将注意力转向 ObjectArray ,JSON 提供的唯一重要数据类型。这些更有趣。 Object s 在 aeson 中用 表示懒惰 HashMap .懒人HashMap s 只评估他们的 WHNF 键,而不是他们的值,所以这些值很可能仍然是未评估的 thunk。同样,Array s 是 Vector s,它们的值也不严格。这两种 Value s 可以包含 thunk。

考虑到这一点,我们知道,一旦我们有了 Value ,唯一的地方decodedecode'不同之处可能在于对象和数组的产生。

观察差异

我们可以尝试的下一件事是实际评估 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 ()
:}

接下来,让我们实际解析一些 JSON:
let jsonDocument = "{ \"value\": [1, { \"value\": [2, 3] }] }" :: ByteString

let !parsed = decode jsonDocument :: Maybe Value
let !parsed' = decode' jsonDocument :: Maybe Value
force parsed
force parsed'

现在我们有两个绑定(bind), parsedparsed' ,其中之一被解析为 decode另一个是 decode' .他们被迫使用 WHNF,所以我们至少可以看到它们是什么,但我们可以使用 :sprint GHCi 中的命令以查看每个值的实际评估量:
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 _))))

你看看那个!用 decode 解析的版本仍然未评估,但解析为 decode'有一些数据。这导致我们发现两者之间的第一个有意义的区别: decode'将其立即结果强制给 WHNF,但 decode推迟它,直到它需要。

让我们看看这些值的内部,看看我们是否找不到更多的差异。一旦我们评估这些外部对象会发生什么?
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 _)))]

另一个区别!对于用 decode 解码的数组,这些值不是强制的,而是用 decode' 解码的值是。如您所见,这意味着 decode在真正需要它们之前,它实际上不会执行到 Haskell 值的转换,这就是文档所说的“延迟转换”的意思。

影响

显然,这两个函数略有不同,很明显, decode'decode更严格.但是,有什么有意义的区别?你什么时候更喜欢一个?

嗯,值得一提的是 decode做的工作永远不会超过 decode' , 所以 decode可能是正确的默认值。当然, decode'永远不会做比 decode 多得多的工作,因为在生成任何值之前需要解析整个 JSON 文档。唯一显着的区别是 decode避免分配 Value s 如果实际上只使用了 JSON 文档的一小部分。

当然,懒惰也不是免费的。懒惰意味着添加 thunk,这会花费空间和时间。如果所有的 thunk 都将被评估,那么 decode只是浪费内存和运行时添加无用的间接。

从这个意义上说,您可能想要使用 decode' 的情况是整个 Value的情况无论如何,结构将被强制,这可能取决于 FromJSON你正在使用的实例。一般来说,我不会担心在它们之间进行选择,除非性能真的很重要并且您正在解码大量 JSON 或在紧密循环中进行 JSON 解码。无论哪种情况,您都应该进行基准测试。选择 decodedecode'是一个非常具体的手动优化,我不会非常有信心在没有基准测试的情况下,这两者实际上会改善我的程序的运行时特性。

关于json - aeson 包中的解码和解码功能有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45374472/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com