英文:
Very Confusing variable changes
问题
我已经为您翻译了代码部分,如下所示:
我在Go语言中有这段上下文无关文法的代码。
我已经反复查看了这段代码很多次,但仍然看不到任何原因导致结构体的值发生改变。有人能看出为什么会发生以下更改吗?
规则:
S -> . [DP VP]
VP -> . [V DP]
VP -> . [V DP AdvP]
在运行以下代码行后:
or2 = append(or2, OstarCF([]QRS{q}, []string{"sees"}, g2.Nullables(), g2.ChainsTo(g2.Nullables()))...)
不知何故,我的结构体值发生了改变...我不知道为什么...
规则:
S -> . [VP VP]
VP -> . [DP DP]
VP -> . [AdvP AdvP AdvP]
这应该与上面的相同。
规则:
S -> DP,VP
VP -> V,DP
VP -> V,DP,AdvP
or2 := []QRS{}
g2 := ToGrammar(cfg2)
fmt.Printf("%s\n", g2)
for _, rule := range g2.Rules {
q := QRS{
one: rule.Src,
two: []string{},
three: rule.Right,
}
or2 = append(or2, OstarCF([]QRS{q}, []string{"sees"}, g2.Nullables(), g2.ChainsTo(g2.Nullables()))...)
}
fmt.Printf("%s\n", g2)
如您所见,我没有使用任何指针变量rule,它们只用于实例化另一个结构体值,但为什么原始结构体字段rule发生了改变?函数OstarCF对字段rule没有做任何操作。
func OstarCF(Qs []QRS, R []string, nD map[string]bool, cD map[string][]string) []QRS {
symbols := []string{}
for _, r := range R {
symbols = append(symbols, cD[r]...)
}
product := []QRS{}
for _, Q := range Qs {
a := Q.one
b := Q.two
c := Q.three
if len(c) > 0 && CheckStr(c[0], symbols) {
b = append(b, c[0])
np := QRS{
one: a,
two: b,
three: c[1:],
}
product = append(product, np)
for len(np.three) > 0 && nD[np.three[0]] == true {
np.two = append(np.two, np.three[0])
np = QRS{
one: np.one,
two: np.two,
three: np.three[1:],
}
product = append(product, np)
}
}
}
return product
}
希望对您有所帮助!如果您有任何其他问题,请随时提问。
英文:
http://play.golang.org/p/Vd3meom5VF
I have this code for some context free grammar in Go
And I am looking at this code so many times and still don't see any reason for the struct values to be changed. Could anybody see why the change like the following happens?
Rules:<br>
S -> . [DP VP]<br>
VP -> . [V DP]<br>
VP -> . [V DP AdvP]<br>
<br>
After I run some functions as in the line
or2 = append(or2, OstarCF([]QRS{q}, []string{"sees"}, g2.Nullables(), g2.ChainsTo(g2.Nullables()))...)
Somehow my struct value is changed... I don't know why...
Rules:<br>
S -> . [VP VP]<br>
VP -> . [DP DP]<br>
VP -> . [AdvP AdvP AdvP]<br>
This should have been same as above.
Rules:
S -> DP,VP
VP -> V,DP
VP -> V,DP,AdvP
or2 := []QRS{}
g2 := ToGrammar(cfg2)
fmt.Printf("%s\n", g2)
for _, rule := range g2.Rules {
q := QRS{
one: rule.Src,
two: []string{},
three: rule.Right,
}
or2 = append(or2, OstarCF([]QRS{q}, []string{"sees"}, g2.Nullables(), g2.ChainsTo(g2.Nullables()))...)
}
fmt.Printf("%s\n", g2)
As you see, I do not use any pointer the variable <b>rule</b>, and they are only used to instantiate another struct value, but how come the original struct field <b>rule</b> has changed? The function OstarCF does not do anything about this field <b>rule</b>
func OstarCF(Qs []QRS, R []string, nD map[string]bool, cD map[string][]string) []QRS {
symbols := []string{}
for _, r := range R {
symbols = append(symbols, cD[r]...)
}
product := []QRS{}
for _, Q := range Qs {
a := Q.one
b := Q.two
c := Q.three
if len(c) > 0 && CheckStr(c[0], symbols) {
b = append(b, c[0])
np := QRS{
one: a,
two: b,
three: c[1:],
}
product = append(product, np)
for len(np.three) > 0 && nD[np.three[0]] == true {
np.two = append(np.two, np.three[0])
np = QRS{
one: np.one,
two: np.two,
three: np.three[1:],
}
product = append(product, np)
}
}
}
return product
}
答案1
得分: 2
原始的Rules字段发生变化,因为使用了指针和切片(也是引用)。
在调用OstarCF之前,会调用ChainsTo方法。它通过值使用了grammar对象,因此会进行一次拷贝,但是Rules字段是指向Rules的指针切片。因此,当拷贝这个字段时,它仍然指向原始对象的数据。
然后,在ChainsTo方法中,对Rules字段进行循环遍历。它会拷贝Right字段,而Right字段是一个字符串切片(因此它仍然指向原始对象的数据):
rhs := rule.Right
最后,通过对rhs进行切片,声明了一个ns变量:
ns := rhs[:i]
ns = append(ns, rhs[i+1:]...)
在这个阶段,ns变量仍然指向包含原始对象的字符串切片的缓冲区。最初,i=0,所以ns是一个重用缓冲区的空切片。当追加项目时,它们会替换原始数据。
这就是为什么你的数据会发生变化。
你可以通过显式地进行拷贝来解决这个问题,例如将上述代码替换为:
ns := make([]string, 0, len(rhs))
ns = append(ns, rhs[:i]...)
ns = append(ns, rhs[i+1:]...)
Go的切片已经取代了C指针算术,但在某些情况下它们几乎同样危险/误导。
英文:
The original Rules field changes because pointers and slices (which are references as well) are used.
Before calling OstarCF, the ChainsTo method is called. It uses the grammar object by value, so a copy is done, but the Rules field is a slice of pointers on Rules. So when this field is copied, it still points to the data of the original object.
Then, in method ChainsTo, there is a loop on the Rules field. It copies the Right field which is a slice of strings (so it still points to data of the original object):
rhs := rule.Right
Finally, a ns variable is declared by slicing rhs:
ns := rhs[:i]
ns = append(ns, rhs[i+1:]...)
At this stage, the ns variable still points to the buffer containing the slice of strings of the original object. Initially, i=0, so ns is an empty slice reusing the buffer. When items are appended, they replace the original data.
That's why your data are changed.
You can fix this problem by explicitly making a copy, for instance by replacing the above lines by:
ns := make( []string, 0, len(rhs) )
ns = append( ns, rhs[:i]...)
ns = append( ns, rhs[i+1:]...)
Go slices have replaced C pointer arithmetic, but they can be almost as dangerous/misleading in some cases.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论