Golang中如何在另一个函数调用里遍历slice

Golang中如何在另一个函数调用里遍历slice 大家好,我是Go语言新手,写了下面这个函数,但修改没有生效。我知道这是因为paths是按值传递的,但当我尝试按引用传递时,又无法遍历那个切片。我该怎么做呢?

func addNodeIdsToPaths(paths [][]node.Node) {
    for _, path := range paths {
      for _, node := range path {
         ndId = funcToGetNodeId(node) 

         // 下面这个修改应该在此函数作用域外生效
         node.NodeId = ndId
       }  
    }
}

更多关于Golang中如何在另一个函数调用里遍历slice的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

true

更多关于Golang中如何在另一个函数调用里遍历slice的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你也可以这样做:

func addNodeIdsToPaths(paths [][]node.Node) {
    for _, path := range paths {
      for i := range path {
         node := &path[i]
         ndId = funcToGetNodeId(node)

         // 下面的更改应该在此函数作用域之外反映出来
         node.NodeId = ndId
       }
    }
}

你好 @petrus@skillian,感谢你们的帮助。很抱歉之前没有提供完整的函数签名,并且回复得这么晚。我发布这个问题时刚开始接触 Go。

这是 funcToGetNodeId 函数的签名。

func funcToGetNodeId(node node.Node) uint64

是的,你们说得对,我应该使用 node := &path[i] 来反映节点对象中的更改。

skillian:

node := &path[i]
ndId = funcToGetNodeId(node)

根据问题描述:

farhan787:

func addNodeIdsToPaths(paths [][]node.Node) {
    for _, path := range paths {
      for _, node := range path {
         ndId = funcToGetNodeId(node)

         // 下面的更改应该在此函数作用域之外生效
         node.NodeId = ndId
       }
    }
}

这似乎意味着

func funcToGetNodeId(node node.Node) int

这可能会将您的解决方案更改为

node := &path[i]
ndId := funcToGetNodeId(*node)

由于您没有发布代码的关键部分,因此很难给出精确的解答。

您正在更新本地的 range 迭代变量 node

node.NodeId = ndId

您还需要更新切片元素。

node.NodeId = ndId
paths[i][j] = node

类似这样:

	for i, path := range paths {
		for j, node := range path {
			ndId := funcToGetNodeId(node)

			// 下面的更改应该反映在此函数作用域之外
			node.NodeId = ndId
			paths[i][j] = node
		}
	}

Go 语言之旅

Range

for 循环的 range 形式用于遍历切片或映射。

在遍历切片时,每次迭代会返回两个值。第一个是索引,第二个是该索引处元素的副本。


Go 编程语言规范:

For 语句

带有 range 子句的 For 语句

带有 “range” 子句的 “for” 语句会遍历切片的所有条目。对于每个条目,如果存在相应的迭代变量,它会将迭代值分配给这些变量,然后执行代码块。

在Go语言中,切片本身就是一个引用类型(包含指向底层数组的指针),但切片中的结构体元素是按值传递的。你的代码需要直接修改切片中的元素,而不是副本。以下是修正后的代码:

func addNodeIdsToPaths(paths [][]node.Node) {
    for i := range paths {
        for j := range paths[i] {
            ndId := funcToGetNodeId(paths[i][j])
            // 直接修改切片中的元素
            paths[i][j].NodeId = ndId
        }
    }
}

关键点:

  1. 使用索引 ij 直接访问切片元素
  2. range paths 返回的是切片副本,但 paths[i]paths[i][j] 允许直接修改原始数据
  3. 切片是引用类型,所以函数外部的修改会生效

示例验证:

package main

import "fmt"

type Node struct {
    NodeId int
    Value  string
}

func funcToGetNodeId(n Node) int {
    return len(n.Value) * 10
}

func addNodeIdsToPaths(paths [][]Node) {
    for i := range paths {
        for j := range paths[i] {
            ndId := funcToGetNodeId(paths[i][j])
            paths[i][j].NodeId = ndId
        }
    }
}

func main() {
    paths := [][]Node{
        {{Value: "a"}, {Value: "ab"}},
        {{Value: "abc"}, {Value: "abcd"}},
    }
    
    fmt.Println("修改前:", paths)
    addNodeIdsToPaths(paths)
    fmt.Println("修改后:", paths)
}

输出:

修改前: [{{0 a} {0 ab}} {{0 abc} {0 abcd}}]
修改后: [{{10 a} {20 ab}} {{30 abc} {40 abcd}}]

这样修改会直接作用于原始切片,因为:

  • 外层切片 paths 是引用类型
  • 内层切片 paths[i] 也是引用类型
  • 通过索引直接修改结构体字段会改变原始数据
回到顶部