英文:
Golang - Why does this function change the input argument?
问题
我正在学习Golang,我之前是使用PHP的。有时候我对一些核心功能有一些困惑。
具体来说,我正在构建一个纸牌游戏,并创建了一个CardStack类型,其中包含一些方便的方法,可以在纸牌堆中使用(比如玩家的手牌、弃牌堆等),例如DrawCards(...)
、AppendCards(...)
等。
问题是,我有一个函数func (c* CardStack) DrawCards(cards []deck.Card) ([]deck.Card, error)
,它会改变参数cards []deck.Card
,我无法弄清楚为什么会这样,也不知道如何避免这种情况。
这是我的CardStack结构体:
type CardStack struct {
cards []deck.Card
}
这是我的DrawCards
方法:
func (c *CardStack) DrawCards(cards []deck.Card) ([]deck.Card, error) {
return c.getCardsSlice(cards, true)
}
// 返回缺失的卡牌
func (c *CardStack) getCardsSlice(cards []deck.Card, rm bool) ([]deck.Card, error) {
var err error
var returnc = []deck.Card{}
for _, card := range cards {
fmt.Println("BEFORE c.findCard(cards): ")
deck.PrintCards(cards) // 在我的例子中,这将打印出{Kc, 8d},这是我期望的结果
_, err = c.findCard(card, rm) // 在这行之后,cards变量被改变了
fmt.Println("AFTER c.findCard(cards): ")
deck.PrintCards(cards) // 在我的例子中,这将打印出{8d, 8d},这完全不是我期望的结果
if err != nil {
return returnc, err
}
}
return returnc, nil
}
// 期望的输入是类似"Ts"或"2h"的字符串(1. 牌面 2. 花色)
func (c *CardStack) findCard(cc deck.Card, rm bool) (deck.Card, error) {
for i, card := range c.GetCards() {
if cc == card {
return c.cardByIndex(i, rm)
}
}
return deck.Card{}, fmt.Errorf("Card not found")
}
func (c *CardStack) cardByIndex(n int, rm bool) (deck.Card, error) {
if n > len(c.GetCards()) {
return deck.Card{}, fmt.Errorf("Index out of bounds")
}
card := c.GetCards()[n]
if rm {
c.SetCards(append(c.GetCards()[:n], c.GetCards()[n+1:]...))
}
return card, nil
}
为了更好地解释,特别是在getCardsSlice
中调用的findCard(...)
方法会修改原始值(我已经添加了注释来指示发生的位置)。
如果有帮助的话,这是我用于调试的main()
方法的一部分:
// ...
ss, _ := cards.SubStack(1, 3) // ss 现在包含{Kc, 8d}
ss.Print() // 打印{Kc, 8d}
cards.Print() // 打印{5c, Kc, 8d}(在代码的其他地方赋值)
cards.DrawCards(ss) // 从{5c, Kc, 8d}中抽取{Kc, 8d}
cards.Print() // 打印{5c} - 如预期
ss.Print() // 打印{8d, 8d} - ???
我做错了什么,应该如何解决这个问题。
非常感谢任何帮助。
编辑:
整个CardStack文件:http://pastebin.com/LmhryfGc
编辑2:
我打算迟早将其放在GitHub上(希望代码看起来还行),这是链接:https://github.com/d1am0nd/hearths-go/tree/cardstack/redo
英文:
I'm learning Golang and I come from PHP background. I have a bit of trouble understanding some of the core functionalities at times.
Specifically, right now I'm building a Hearths game and I've created a CardStack type, that has some convenient methods one might use in a card stack (read: player's hand, discard pile...) such as DrawCards(...)
, AppendCards(...)
...
The problem I have is that the function func (c* CardStack) DrawCards(cards []deck.Card) ([]deck.Card, error) {...}
changes the argument cards []deck.Card
and I cannot figure out why or how to avoid this.
This is my CardStack:
type CardStack struct {
cards []deck.Card
}
This is my DrawCards
method:
func (c *CardStack) DrawCards(cards []deck.Card) ([]deck.Card, error) {
return c.getCardsSlice(cards, true)
}
// Returns cards that are missing
func (c *CardStack) getCardsSlice(cards []deck.Card, rm bool) ([]deck.Card, error) {
var err error
var returnc = []deck.Card{}
for _, card := range cards {
fmt.Println("BEFORE c.findCard(cards): ")
deck.PrintCards(cards) // In my example this will print out {Kc, 8d}, which is what I expect it to be
_, err = c.findCard(card, rm) // AFTER THIS LINE THE cards VAR IS CHANGED
fmt.Println("AFTER c.findCard(cards): ")
deck.PrintCards(cards) // In my example this will print out {8d, 8d}, which is not at all what I expected
if err != nil {
return returnc, err
}
}
return returnc, nil
}
// Expects string like "Ts" or "2h" (1. face 2. suit)
func (c *CardStack) findCard(cc deck.Card, rm bool) (deck.Card, error) {
for i, card := range c.GetCards() {
if cc == card {
return c.cardByIndex(i, rm)
}
}
return deck.Card{}, fmt.Errorf("Card not found")
}
func (c *CardStack) cardByIndex(n int, rm bool) (deck.Card, error) {
if n > len(c.GetCards()) {
return deck.Card{}, fmt.Errorf("Index out of bounds")
}
card := c.GetCards()[n]
if rm {
c.SetCards(append(c.GetCards()[:n], c.GetCards()[n+1:]...))
}
return card, nil
}
To explain a bit more - specifically the findCard(...)
method that gets called in getCardsSlice
messes with the original value (I've added comments to indicate where it happens).
If it's of any help, this is part of my main()
method that I use for debugging:
// ...
ss, _ := cards.SubStack(1, 3) // ss now holds {Kc, 8d}
ss.Print() // Prints {Kc, 8d}
cards.Print() // Prints {5c, Kc, 8d} (assigned somewhere up in the code)
cards.DrawCards(ss) // Draws {Kc, 8d} from {5c, Kc, 8d}
cards.Print() // Prints {5c} - as expected
ss.Print() // Prints {8d, 8d} - ???
What am I doing wrong and how should I go about doing this.
Any kind of help is appreciated.
Edit:
The whole CardStack file: http://pastebin.com/LmhryfGc
Edit2:
I was going to put it on github sooner or later (was hoping after the code looks semi-ok), here it is - https://github.com/d1am0nd/hearths-go/tree/cardstack/redo
答案1
得分: 5
在你的示例中,DrawCards
函数中的 cards
值是 CardsStack.cards
切片的子切片,它引用了相同的底层数组中的值。
当你调用 findCard
并从 CardStack.cards
切片中移除一张卡片时,你正在操作与 cards
参数使用的相同数组。
当你想要一个切片的副本时,你需要分配一个新的切片并复制每个元素。在你的示例中,你可以这样做:
ssCopy := make([]deck.Card, len(ss))
copy(ssCopy, ss)
cards.DrawCards(ssCopy)
英文:
In your example, the value of cards
in DrawCards
is a sub-slice of the CardsStack.cards
slice, which is referencing values in the same backing array.
When you call findCard
and remove a card from the CardStack.cards
slice, you are manipulating the same array that the cards
argument is using.
When you want a copy of a slice, you need to allocate a new slice and copy each element. To do this in your example, you could:
ssCopy := make([]deck.Card, len(ss))
copy(ssCopy, ss)
cards.DrawCards(ssCopy)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论