Variable modification in golang [Mutability]

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

Variable modification in golang [Mutability]

问题

以下是翻译好的内容:

下面的代码打开一个.txt文件并计算单词频率。我在按照一本书的指导进行操作时感到困惑:

我的问题是:

filename := os.Args[1]
frequencyForWord := map[string]int{}
updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)

我创建了一个名为frequencyForWord的变量,并将其传递给一个不返回任何内容的函数func updateFrequencies

这个函数修改了这个变量,这就是为什么当我执行fmt.Println(frequencyForWord)时,它会显示一个以单词为键,以计数为值的映射。

我的问题是:

为什么我不需要像这样做:

frequencyForWord = updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
// 然后将func updateFrequencies更改为返回一个映射

我认为为了让函数修改一个变量,我需要将变量作为引用传递,像这样updateFrequencies(filename, &frequencyForWord)

原始代码:

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"unicode"
)

func main() {
	if len(os.Args) == 1 || os.Args[1] == "-h" {
		fmt.Printf("usage: %s <file>\n", filepath.Base(os.Args[0]))
		os.Exit(1)
	}
	filename := os.Args[1]
	frequencyForWord := map[string]int{}
	updateFrequencies(filename, frequencyForWord)
	fmt.Println(frequencyForWord)
}

func updateFrequencies(filename string, frequencyForWord map[string]int) string {
	file, err := os.Open(filename)
	if err != nil {
		log.Printf("Failed to open the file: %s.", filename)
	}
	defer file.Close()
	readAndUpdateFrequencies(bufio.NewScanner(file), frequencyForWord)
}

func readAndUpdateFrequencies(scanner *bufio.Scanner, frequencyForWord map[string]int) {
	for scanner.Scan() {
		for _, word := range SplitOnNonLetter(strings.TrimSpace(scanner.Text())) {
			frequencyForWord[strings.ToLower(word)] += 1
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}

func SplitOnNonLetter(line string) []string {
	nonLetter := func(char rune) bool { return !unicode.IsLetter(char) }
	return strings.FieldsFunc(line, nonLetter)
}
英文:

The below code opens up a .txt file and counts the word frequencies. I am following a book and I got confused:

My question is here:

filename := os.Args[1]
frequencyForWord := map[string]int{}
updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)

I create a variable called frequencyForWord and pass it into a function that does not return anything called func updateFrequencies

This function modifies the variable and that's why when I do fmt.Println(frequencyForWord) it shows me a map that has words as keys and their counts as values.

My question is:

why don't I have to do something like this

frequencyForWord = updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
// And then change func updateFrequencies to something to returns a map

I thought in order for a function to modify a variable I need to pass in the variable as a reference like this updateFrequencies(filename, &amp;frequencyForWord)

Original Code:

package main
import(
&quot;fmt&quot;
&quot;path/filepath&quot;
&quot;os&quot;
&quot;log&quot;
&quot;bufio&quot;
&quot;strings&quot;
&quot;unicode&quot;
)
func main() {
if len(os.Args) == 1 || os.Args[1] == &quot;-h&quot; {
fmt.Printf(&quot;usage: %s &lt;file&gt;\n&quot;, filepath.Base(os.Args[0]))
os.Exit(1)
}
filename := os.Args[1]
frequencyForWord := map[string]int{}
updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
}
func updateFrequencies(filename string, frequencyForWord map[string]int) string {
file, err := os.Open(filename)
if err != nil {
log.Printf(&quot;Failed to open the file: %s.&quot;, filename)
}
defer file.Close()
readAndUpdateFrequencies(bufio.NewScanner(file), frequencyForWord)
}
func readAndUpdateFrequencies(scanner *bufio.Scanner, frequencyForWord map[string]int) {
for scanner.Scan() {
for _, word := range SplitOnNonLetter(strings.TrimSpace(scanner.Text())) {
frequencyForWord[strings.ToLower(word)] += 1
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
func SplitOnNonLetter(line string) []string {
nonLetter := func(char rune) bool { return !unicode.IsLetter(char) }
return strings.FieldsFunc(line, nonLetter)
}

答案1

得分: 7

因为地图结构本身不包含值,而是指向保存值的结构。

文档中所述:

像切片一样,地图保存对底层数据结构的引用。如果将地图传递给更改地图内容的函数,更改将在调用者中可见。

这就像当你将指针传递给函数时:它允许函数更改你的值。

这里有另一个相同现象的例子:

type A struct {
b *B
}
type B struct {
c int
} 
func incr(a A) {
a.b.c++
}
func main() {
a := A{}
a.b = new(B)
fmt.Println(a.b.c) // 输出 0
incr(a)
fmt.Println(a.b.c) // 输出 1
}
英文:

Because the map structure doesn't contain the values itself but points to the structures holding the values.

As written in the documentation :

> Like slices, maps hold references to an underlying data structure. If
> you pass a map to a function that changes the contents of the map, the
> changes will be visible in the caller.

That's just like when you pass a pointer to a function : it lets the function change your value.

Here's another example of the same phenomenon :

type A struct {
b *B
}
type B struct {
c int
} 
func incr(a A) {
a.b.c++
}
func main() {
a := A{}
a.b = new(B)
fmt.Println(a.b.c) // prints 0
incr(a)
fmt.Println(a.b.c) // prints 1
}

答案2

得分: 2

该函数并不修改变量,而是修改绑定到该变量的。这是因为map是一个可变的数据结构,并且将其传递给函数时并不会复制该结构。(map隐式地是对哈希表的引用,并且该引用被传递到各处。)

英文:

The function is not modifying the variable, but the value bound to the variable. That's possible because a map is a mutable data structure and passing it to a function does not copy the structure. (A map is implicitly a reference to a hash table and the reference is passed around.)

huangapple
  • 本文由 发表于 2013年9月9日 20:45:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/18698530.html
匿名

发表评论

匿名网友

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

确定