英文:
Is there a way to serialize cyclic data structures with encoding/gob?
问题
我正在将一个神经网络库移植到Go语言。我想要能够保存和恢复训练好的网络,所以我试图直接对其进行序列化。问题是,网络结构中的字段存在循环引用(神经元A连接到神经元B,神经元B连接到神经元A)。每当我尝试使用encoding/gob对整个网络进行序列化时,都会出现堆栈溢出的错误。
以下是一个简单的代码示例,以相同的方式出错:
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type P struct {
Name string
Q *Q
}
type Q struct {
Name string
P *P
}
func main() {
var network bytes.Buffer // 用于模拟网络连接
enc := gob.NewEncoder(&network) // 将写入网络
dec := gob.NewDecoder(&network) // 将从网络读取
p := &P{"P", nil}
q := &Q{"Q", p}
p.Q = q
err := enc.Encode(p)
if err != nil {
log.Fatal("encode error:", err)
}
// 解码(接收)值
var p2 *P
err = dec.Decode(&p2)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Printf("%#v", p2)
}
除了重写整个库的结构以避免循环引用之外,是否有一种简单的方法来解决这个问题?
谢谢。
英文:
I'm working on porting a neural network library to Go. I want to be able to save and restore a trained network, so I'm attempting to serialize it directly. The problem is, the network struct contains cycles in its field (Neuron A has a connection to Neuron B, which has a connection to Neuron A). Whenever I try to serialize the entire network with encoding/gob, it fails with a stackoverflow.
Here's a very simple example of code that breaks in the same way:
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type P struct {
Name string
Q *Q
}
type Q struct {
Name string
P *P
}
func main() {
var network bytes.Buffer // Stand-in for a network connection
enc := gob.NewEncoder(&network) // Will write to network.
dec := gob.NewDecoder(&network) // Will read from network.
p := &P{ "P", nil }
q := &Q{ "Q", p }
p.Q = q
err := enc.Encode(p)
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) the value.
var p2 *P
err = dec.Decode(&p2)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Printf("%#v", p2)
}
http://play.golang.org/p/LrO0VlLnX4
Barring rewriting the entire structure of the library to avoid cycles, is there a straightforward way to get around this problem?
Thanks
答案1
得分: 6
你不能直接使用gob,但不要担心,勇敢的世界公民!
你可以在你的类型上实现BinaryMarshaler
/BinaryUnmarshaler
接口作为解决方法,当gob对你的类型进行编码/解码时,它将愉快地使用它们。
func (p *P) MarshalBinary() (_ []byte, err error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
enc.Encode(p.Name)
if p.Q == nil {
return buf.Bytes(), nil
}
isCyclic := p.Q != nil && p.Q.P == p
enc.Encode(isCyclic)
if isCyclic {
p.Q.P = nil
err = enc.Encode(p.Q)
p.Q.P = p
} else {
err = enc.Encode(p.Q)
}
return buf.Bytes(), err
}
func (p *P) UnmarshalBinary(data []byte) (err error) {
dec := gob.NewDecoder(bytes.NewReader(data))
if err = dec.Decode(&p.Name); err != nil {
return
}
var isCyclic bool
if err = dec.Decode(&isCyclic); err != nil {
return
}
err = dec.Decode(&p.Q)
if isCyclic {
p.Q.P = p
}
return
}
警告 每次创建新的解码器/编码器都非常低效,你可能需要考虑使用binary.*
。
英文:
You can't use gob directly, but fear not brave citizen of the world!
You can implement the BinaryMarshaler
/BinaryUnmarshaler
interfaces on your type as a work around and gob will happily use them instead when it's encoding/decoding your type.
func (p *P) MarshalBinary() (_ []byte, err error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
enc.Encode(p.Name)
if p.Q == nil {
return buf.Bytes(), nil
}
isCyclic := p.Q != nil && p.Q.P == p
enc.Encode(isCyclic)
if isCyclic {
p.Q.P = nil
err = enc.Encode(p.Q)
p.Q.P = p
} else {
err = enc.Encode(p.Q)
}
//buf.Encode
return buf.Bytes(), err
}
func (p *P) UnmarshalBinary(data []byte) (err error) {
dec := gob.NewDecoder(bytes.NewReader(data))
if err = dec.Decode(&p.Name); err != nil {
return
}
var isCyclic bool
if err = dec.Decode(&isCyclic); err != nil {
return
}
err = dec.Decode(&p.Q)
if isCyclic {
p.Q.P = p
}
return
}
warning creating a new decoder/encoder every time is extremely inefficient, you might want to look into using binary.*
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论