When populating a slice with length set at runtime, do I absolutely need two loops, one for determining the length and one for populating?

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

When populating a slice with length set at runtime, do I absolutely need two loops, one for determining the length and one for populating?

问题

所以我有一个函数,它从字符串中删除标点符号,并将这些标点符号及其索引放入两个切片中:

func removeAndIndexPunctuation(word string) (string, []rune, []int) {
    // Index punctuation
    numberOfPunct := 0
    for _, char := range word {
        if unicode.IsPunct(char) {
            numberOfPunct += 1
        }
    }

    punctuations := make([]rune, numberOfPunct)
    punctuationIndex := make([]int, numberOfPunct)

    x := 0
    for i, char := range word {
        if unicode.IsPunct(char) {
            punctuations[x] = char
            punctuationIndex[x] = i
            x += 1
        }
    }
    
    // Remove all punctuation from word string
    res := r.ReplaceAllString(word, "")
    return res, punctuations, punctuationIndex
}

为了创建和填充这两个切片,我必须运行两个循环,一个用于计算标点符号的数量,以便我可以创建正确长度的数组,然后另一个循环基本上相同,只是现在我填充了这两个切片。

然而,在Python中,我不需要两个循环,因为Python支持"动态数组":

def removeAndIndexPunctuation(word):
    punctuations = []
    # Index punctuation
    for i, char in enumerate(word):
        if char in string.punctuation:
            punctuations.append((char, i))
    # Remove all punctuation from word string
    word = word.encode("utf-8").translate(None, string.punctuation).decode("utf-8")
    return word, punctuations

所以我只是想确认一下,在这种情况下,在Go中,我是否绝对需要两个循环,因为它不支持动态数组,或者我是否遗漏了什么?换句话说,如果我正在遍历一组字符并将其中一些添加到数组/切片中,我是否真的需要两个循环,一个用于计算字符的数量以设置切片的长度,另一个用于填充切片?

我来自Python,正在学习Go。

英文:

So I have a function that removes punctuation from a string and puts those punctuation characters and their index into two slices:

func removeAndIndexPunctuation(word string) (string, []rune, []int) {
	// Index punctuation
	numberOfPunct := 0
	for _, char := range word {
		if unicode.IsPunct(char) {
			numberOfPunct += 1
		}
	}

	punctuations := make([]rune, numberOfPunct)
	punctuationIndex := make([]int, numberOfPunct)

	x := 0
	for i, char := range word {
		if unicode.IsPunct(char) {
			punctuations[x] = char
			punctuationIndex[x] = i
			x += 1
		}
	}
	
	// Remove all punctuation from word string
	res := r.ReplaceAllString(word, "")
	return res, punctuations, punctuationIndex
}

In order to make and populate the slices I have to run two for loops, one for counting the number of punctuations so I can make the array the correct length and then another that's pretty much the same except now I populate the slices.

In Python though I don't need two for loops since Python supports "dynamic arrays":

def removeAndIndexPunctuation(word):
	punctuations = []
	# Index punctuation
	for i, char in enumerate(word):
		if char in string.punctuation:
			punctuations.append((char, i))
	# Remove all punctuation from word string
	word = word.encode("utf-8").translate(None, string.punctuation).decode("utf-8")
	return word, punctuations

So I just want to make sure, in this case in golang, do I absolutely need two for loops because it doesn't support dynamic arrays or am I missing something? Or in other words, If I'm looping over a set of characters and adding some to an array/slice, do I really need two loops, one for counting the number of characters for setting the length of the slice, and one for populating the slice?

I come from Python and am learning Go.

答案1

得分: 3

你不需要这样做。Golang的切片是动态数组(不要将它们与实际的数组混淆)。你应该(重新)阅读关于它的优秀的golang切片内部原理博文。

以下是用更符合惯用方式重写的示例:

func removeAndIndexPunctuation(word string) (string, []rune, []int) {
    var punctuations []rune
    var indexes []int

    for i, char := range word {
        if unicode.IsPunct(char) {
            punctuations = append(punctuations, char)
            indexes = append(indexes, i)
        }
    }

    // 从单词字符串中删除所有标点符号
    res := r.ReplaceAllString(word, "")
    return res, punctuations, indexes
}

请注意,我认为在这里使用regex并不特别相关。以下是使用切片的另一个版本:

func removeAndIndexPunctuation(word string) (string, []rune, []int) {
    var punctuations []rune
    var indexes []int
    var result []rune

    for i, char := range word {
        if unicode.IsPunct(char) {
            punctuations = append(punctuations, char)
            indexes = append(indexes, i)
        } else {
            result = append(result, char)
        }
    }

    return string(result), punctuations, indexes
}
英文:

You don't. Golang slices are dynamic arrays (don't confuse them for the actual arrays). You should (re-)read the excellent golang slice internals blog post about it.

Here is your example rewritten in a more idiomatic way:

func removeAndIndexPunctuation(word string) (string, []rune, []int) {
	var punctuations []rune
	var indexes []int

	for i, char := range word {
		if unicode.IsPunct(char) {
			punctuations = append(punctuations, char)
			indexes = append(indexes, i)
		}
	}

	// Remove all punctuation from word string
	res := r.ReplaceAllString(word, "")
	return res, punctuations, indexes
}

Note that I don't think the use of regex is particularly relevant here. Here is another version using a slice of runes:

func removeAndIndexPunctuation(word string) (string, []rune, []int) {
	var punctuations []rune
	var indexes []int
	var result []rune

	for i, char := range word {
		if unicode.IsPunct(char) {
			punctuations = append(punctuations, char)
			indexes = append(indexes, i)
		} else {
			result = append(result, char)
		}
	}

	return string(result), punctuations, indexes
}

huangapple
  • 本文由 发表于 2015年3月3日 19:53:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/28831158.html
匿名

发表评论

匿名网友

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

确定