Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Pointers 对相同数据和内存分配的引用 请考虑以下数据模型: data Artist = Artist Text data Song = Song Artist Text data Catalogue = Catalogue (Set Artist) (Set Song)_Pointers_Haskell_Memory - Fatal编程技术网

Pointers 对相同数据和内存分配的引用 请考虑以下数据模型: data Artist = Artist Text data Song = Song Artist Text data Catalogue = Catalogue (Set Artist) (Set Song)

Pointers 对相同数据和内存分配的引用 请考虑以下数据模型: data Artist = Artist Text data Song = Song Artist Text data Catalogue = Catalogue (Set Artist) (Set Song),pointers,haskell,memory,Pointers,Haskell,Memory,您可以看到艺术家s是从歌曲s和目录中引用的。目录包含从歌曲s中引用的所有艺术家的列表,因此艺术家的相同值可以从两个地方引用 假设我们使用以下函数的多个应用程序生成目录值: insertSong :: Song -> Catalogue -> Catalogue insertSong song@(Song artist title) (Catalogue artists songs) = Catalogue (Set.insert artist artists) (Set.inse

您可以看到
艺术家
s是从
歌曲
s和
目录
中引用的。
目录
包含从
歌曲
s中引用的所有艺术家的列表,因此
艺术家
的相同值可以从两个地方引用

假设我们使用以下函数的多个应用程序生成
目录
值:

insertSong :: Song -> Catalogue -> Catalogue
insertSong song@(Song artist title) (Catalogue artists songs) =
  Catalogue (Set.insert artist artists) (Set.insert song songs)
显然,
目录
将通过引用与
歌曲
相同的
艺术家
值来填充,从而通过不存储这些值的副本来节省内存

问题是,当我试图通过分别对一组艺术家和一组歌曲进行反序列化,从序列化数据重新创建目录时,应用程序占用的内存要比使用
insertSong
生成相同的
catalog
值时多得多。我怀疑这是由于从
歌曲
s中引用的同一
艺术家
s与
目录
之间的关系丢失造成的,这就是为什么我得到了
艺术家
的值副本,占用了额外的内存

我看到的唯一解决方案是首先反序列化艺术家集,然后反序列化歌曲集,同时强制将
Artist
的值替换为第一组中的值

因此,我的问题是:

  • 我的怀疑正确吗
  • 我看到的解决方案有效吗
  • 有没有更好的办法解决这个问题
  • 听起来很有道理
  • 如果做得好,它应该会起作用。特别是,您必须确保所有内容都经过认真评估,以避免引用thunks中的旧文本值
  • 您可以选择更智能的序列化格式。例如,序列化歌曲时,将艺术家索引存储在艺术家列表中,而不是艺术家全名。然后在反序列化过程中查找它

  • 请注意,如果您对字符串进行任何类型的计算,共享也将丢失(即,即使
    artist1
    artist2
    相同且共享,
    f artist1
    f artist2
    可能不相同)。如果这成为一个问题,您也可以对数据结构进行类似的更改。

    一个简单的解决方案似乎是使用某种退化的映射缓存数据:

    {-# LANGUAGE DeriveDataTypeable, RankNTypes #-}
    import Control.Monad
    import Control.Monad.State
    import Data.Map (Map)
    import qualified Data.Map as M
    
    type Cache a = Map a a
    
    然后,如果已经存在与此缓存相同的条目,我们可以查询此缓存,并将其替换为缓存的条目:

    cached :: (Ord a) => a -> State (Cache a) a
    cached x = state $ \m ->
        case M.lookup x m of
            Just x'     -> (x', m)
            Nothing     -> (x, M.insert x x m)
    
    这样,如果我们加载几个类型为
    a
    的相等元素,我们将它们转换为单个元素。这可以在反序列化过程中完成,也可以在最后完成一次


    也许可以进一步推广它,并使用SYB通过缓存映射数据结构中某些给定类型的所有值:

    import Data.Data (Data)
    import Data.Generics.Aliases (mkM)
    import Data.Generics.Schemes (everywhereM)
    import Data.Typeable (Typeable)
    
    replaceFromCache
        :: (Ord a, Typeable a, Data b)
        => b -> State (Cache a) b
    replaceFromCache = everywhereM (mkM cached)
    
    然后我们可以替换某些数据结构中的所有艺术家,如

    data Artist = Artist String
      deriving (Eq, Ord, Typeable)
    
    cacheAllArtists :: (Data b) => b -> b
    cacheAllArtists b = evalState (replaceFromCache b) (M.empty :: Cache Artist)
    
    或者我们可以使用phantom type创建通用版本:

    cacheAll :: (Ord a, Typeable a, Data b)
          => Proxy a -> b -> b
    cacheAll p = flip evalState (emptyOf p) . replaceFromCache
      where
        emptyOf p = asTypeOf2 M.empty p
        asTypeOf2 :: f a -> Proxy a -> f a
        asTypeOf2 = const
    
    cacheAllArtists :: (Data b) => b -> b
    cacheAllArtists = cacheAll (Proxy :: Proxy Artist)
    

    (免责声明:我没有测试上面的任何代码。)

    我偶然发现了一个项目,它解决了这个问题。参见。

    关于泛型的想法非常有趣。这个问题对于开发这样一个图书馆来说已经足够普遍了。谢谢我会调查的。@NikitaVolkov我很乐意参与。太好了!虽然我必须承认,我还没有准备好参与这样一个项目,但如果我能回到这个项目,我会与你保持联系。我偶然发现了一个项目,它接近这个问题。看见