有没有一种方法可以使用encoding/gob序列化循环数据结构?

huangapple go评论97阅读模式
英文:

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
}

<kbd>playground</kbd>

warning creating a new decoder/encoder every time is extremely inefficient, you might want to look into using binary.*.

huangapple
  • 本文由 发表于 2015年2月23日 08:50:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/28665403.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定