【发布时间】:2018-08-31 21:27:39
【问题描述】:
我知道在 Go 中一切都是按值传递的,这意味着如果我给一个函数一个切片,并且该函数使用内置的 append 函数附加到切片,那么原始切片将没有附加的值函数的范围。
例如:
nums := []int{1, 2, 3}
func addToNumbs(nums []int) []int {
nums = append(nums, 4)
fmt.Println(nums) // []int{1, 2, 3, 4}
}
fmt.Println(nums) // []int{1, 2, 3}
这给我带来了一个问题,因为我试图在一个累积切片上进行递归,基本上是一个 reduce 类型的函数,除了 reducer 调用自身。
这是一个例子:
func Validate(obj Validatable) ([]ValidationMessage, error) {
messages := make([]ValidationMessage, 0)
if err := validate(obj, messages); err != nil {
return messages, err
}
return messages, nil
}
func validate(obj Validatable, accumulator []ValidationMessage) error {
// If something is true, recurse
if something {
if err := validate(obj, accumulator); err != nil {
return err
}
}
// Append to the accumulator passed in
accumulator = append(accumulator, message)
return nil
}
上面的代码给出了与第一个示例相同的错误,因为 accumulator 没有获取所有附加值,因为它们只存在于函数的范围内。
为了解决这个问题,我将一个指针结构传递给函数,该结构包含累加器。该解决方案效果很好。
我的问题是,有没有更好的方法来做到这一点,我的方法是 Go 的惯用方法吗?
更新的解决方案(感谢 icza):
我只是在递归函数中返回切片。这么大的脸,早该想到的。
func Validate(obj Validatable) ([]ValidationMessage, error) {
messages := make([]ValidationMessage, 0)
return validate(obj, messages)
}
func validate(obj Validatable, messages []ValidationMessage) ([]ValidationMessage, error) {
err := v.Struct(obj)
if _, ok := err.(*validator.InvalidValidationError); ok {
return []ValidationMessage{}, errors.New(err.Error())
}
if _, ok := err.(validator.ValidationErrors); ok {
messageMap := obj.Validate()
for _, err := range err.(validator.ValidationErrors) {
f := err.StructField()
t := err.Tag()
if v, ok := err.Value().(Validatable); ok {
return validate(v, messages)
} else if _, ok := messageMap[f]; ok {
if _, ok := messageMap[f][t]; ok {
messages = append(messages, ValidationMessage(messageMap[f][t]))
}
}
}
}
return messages, nil
}
【问题讨论】:
-
返回新切片,就像内置的
append()一样(并在调用者处分配返回值),或者传递一个指向切片的指针,而不是切片头。 -
我无法返回新切片,因为我必须对切片进行递归处理,但我认为传递一个指向轻微的指针可能有效?让我试试..
-
递归调用也可以返回值,我看不出有什么问题。如果您使用传递切片指针,请注意处理它们更“冗长”,请参阅Slicing a slice pointer passed as argument 作为示例。
-
你知道吗,你是完全正确的。不知道我在想什么。从递归函数返回切片对我来说效果很好。非常感谢。
-
@OmarIlias 传递切片标头足够快,因为它很小。如果你传递一个指向它的指针,传递可能会更快,但为了使用它,你必须“不断地”(在每次使用时)取消引用它,代码会更丑陋,所以你会失去优势。想一想:内置的
append()可以被编写为获取一个指针,并且它不需要返回新切片(并且您不必分配回结果),但它在另一个上失败了需要切片的路径,而不是指向切片的指针。
标签: go