Сериализация взаимно-рекурсивных данных
Feb. 5th, 2010 01:23 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
На RSDN Tonal- задаёт вопрос о сериализации взаимно-рекурсивных данных.
Очевидно, что при сериализации рекурсивных данных необходимо не сериализовать уже сериализованные данные, дабы избежать цикла. Для этого в том месте, где имеется связь со следующим рекурсивным звеном, можно сохранить ссылку.
Поэтому в голову приходит замечательный пакет data-reify от Andy Gill. О его применении можно прочитать в статье Type-Safe Observable Sharing in Haskell.
Смысл в том, что мы можем разложить рекурсивные данные на нерекурсивные данные со ссылками - то, что нам и нужно.
Т.е. для типов из примера Tonal-
это будет
Обратите внимание на дырки
Пишем instance для MuRef
и получаем нужный граф, например
или
Меня здесь смущает тип
При десериализации придётся обращать граф в конкретный тип, например,
Покритикуйте.
Очевидно, что при сериализации рекурсивных данных необходимо не сериализовать уже сериализованные данные, дабы избежать цикла. Для этого в том месте, где имеется связь со следующим рекурсивным звеном, можно сохранить ссылку.
Поэтому в голову приходит замечательный пакет data-reify от Andy Gill. О его применении можно прочитать в статье Type-Safe Observable Sharing in Haskell.
Смысл в том, что мы можем разложить рекурсивные данные на нерекурсивные данные со ссылками - то, что нам и нужно.
Т.е. для типов из примера Tonal-
data A = A {a_unuqId :: Int, a_refs :: [B]} data B = B {b_unuqId :: Int, b_refs :: [A]}
это будет
data N u = AN { an_uniqId :: Int, an_refs :: [u] } | BN { bn_uniqId :: Int, bn_refs :: [u] }
Обратите внимание на дырки
`u'
, оставленные для рекурсии - по сути A ~ N B
и наоборот.Пишем instance для MuRef
instance MuRef A where type DeRef A = N mapDeRef f (A i rs) = AN i <$> sequenceA (map f rs) instance MuRef B where type DeRef B = N mapDeRef f (B i rs) = BN i <$> sequenceA (map f rs)
и получаем нужный граф, например
> let { a = A 1 [b] ; b = B 2 [a] } > reifyGraph a let [(1,AN {an_uniqId = 1, an_refs = [2]}),(2,BN {bn_uniqId = 2, bn_refs = [1]})] in 1
или
> let { a1 = A 1 [b1, b2] ; a2 = A 2 [b2] ; b1 = B 3 [a1] ; b2 = B 4 [a1, a2] } > reifyGraph a1 let [(1,AN {an_uniqId = 1, an_refs = [2,3]}), (3,BN {bn_uniqId = 4, bn_refs = [1,4]}), (4,AN {an_uniqId = 2, an_refs = [3]}), (2,BN {bn_uniqId = 3, bn_refs = [1]})] in 1
Меня здесь смущает тип
[(Unique, g Unique)]
, всё таки хочется IntMap (g Unique)
- быстрее и десериализация будет проще. Впрочем, переписать библиотеку должно быть несложно.При десериализации придётся обращать граф в конкретный тип, например,
A
. Это дополнительные телодвижения, но зато весь код у нас более общий и не привязан конкретно к сериализации. Например, необходимо использовать мемоизацию:graphToA :: Graph (DeRef A) -> A graphToA (Graph gs i) = ma ! i where ma = fromList [ (i, A ai $ map (mb!) rs) | (i, AN ai rs) <- gs ] mb = fromList [ (i, B bi $ map (ma!) rs) | (i, BN bi rs) <- gs ]
Покритикуйте.
no subject
Date: 2010-02-05 01:55 pm (UTC)Надо будет подумать, как это получше написать.
no subject
Date: 2010-02-05 02:01 pm (UTC)no subject
Date: 2010-02-05 02:30 pm (UTC)no subject
Date: 2010-02-05 01:56 pm (UTC)no subject
Date: 2010-02-05 02:02 pm (UTC)[ id | A id _ <- universeBi a ]
чтобы получить _конечный_ список всех нужных полей.
спасибо
Date: 2010-02-05 02:41 pm (UTC)no subject
Date: 2010-02-05 06:22 pm (UTC)no subject
Date: 2010-02-06 07:07 am (UTC)no subject
Date: 2010-02-06 11:09 am (UTC)для
data A = A {a_unuqId :: Int, a_refs :: [B]}
data B = B {b_unuqId :: Int, b_refs :: [A]}
сериализуем в монаде
type Ser a = StateT (Set.Set Int) (Writer [Word8]) a
посещённые Id добавляем в стейт, рекурсивный обход можно делать чем угодно, главное, чтоб было удобно.
Чем плохо такое решение в лоб ?
no subject
Date: 2010-02-06 09:44 pm (UTC)no subject
Date: 2010-02-07 07:36 am (UTC)no subject
Date: 2010-02-07 07:37 am (UTC)no subject
Date: 2010-02-07 07:57 am (UTC)no subject
Date: 2010-02-07 08:50 am (UTC)no subject
Date: 2010-02-07 11:40 am (UTC)Тест:
Десериализацию с ходу не напишу, надо будет gread покурить. По идее, пишется.
no subject
Date: 2010-02-08 06:58 am (UTC)Версия, которая не зависит от Id (пользую StableName)
http://hpaste.org/fastcgi/hpaste.fcgi/view?id=17329#a17329
В uniplate, чтобы поддерживал рекурсивные данные, это красиво не засунешь :-( Потому что IO.
no subject
Date: 2010-02-08 07:06 am (UTC)no subject
Date: 2010-02-08 07:08 am (UTC)Работаем...
no subject
Date: 2010-02-08 07:12 am (UTC)no subject
Date: 2010-02-08 12:34 pm (UTC)no subject
Date: 2010-02-08 01:09 pm (UTC)no subject
Date: 2010-02-08 03:17 pm (UTC)no subject
Date: 2010-02-08 04:05 pm (UTC)http://haskell.org/haskellwiki/Things_to_avoid - тут не форматирование
Ещё есть соглашения для GHC sources, оттуда тоже можно взять что-нибудь полезное.
Поиск haskell beautifier не дал ничего полезного :(
no subject
Date: 2010-02-08 05:55 pm (UTC)no subject
Date: 2010-02-05 08:14 pm (UTC)no subject
Date: 2010-02-05 09:39 pm (UTC)no subject
Date: 2010-02-05 10:10 pm (UTC)а оно уже работает?
Date: 2010-02-05 10:43 pm (UTC)Re: а оно уже работает?
Date: 2010-02-05 11:14 pm (UTC)Я думаю, мы переживём отсутствие автодокументации. Но даже если оно не скомпилится - идея там достаточно простая, можно и самостоятельно воспроизвести.
Re: а оно уже работает?
Date: 2010-02-05 11:17 pm (UTC)Re: а оно уже работает?
Date: 2010-02-06 12:03 am (UTC)no subject
Date: 2010-02-06 08:29 am (UTC)