Recursion Go中的递归
我是一名Javascript开发人员,我决定试一试。作为一个学习练习,我决定在我的一个节点项目中移植一个函数,但却无法让它在我的一生中正常工作。该函数的目的是显示所有有效的英语单词,这些单词可以由不同单词中的字母组成(我正在构建一个多人版的)。例如,findAllWords(“dances”)将返回['can'、'scan'、'dance'、'dances'等]。我通过递归从英语单词列表构建的trie来实现这一点 以下是函数在Javascript中的实现:Recursion Go中的递归,recursion,go,Recursion,Go,我是一名Javascript开发人员,我决定试一试。作为一个学习练习,我决定在我的一个节点项目中移植一个函数,但却无法让它在我的一生中正常工作。该函数的目的是显示所有有效的英语单词,这些单词可以由不同单词中的字母组成(我正在构建一个多人版的)。例如,findAllWords(“dances”)将返回['can'、'scan'、'dance'、'dances'等]。我通过递归从英语单词列表构建的trie来实现这一点 以下是函数在Javascript中的实现: self.findAllWords =
self.findAllWords = function(letters = [], trie = dictionary, curString = '') {
let words = [];
letters = typeof letters === 'string' ? letters.split('') : letters;
letters.forEach( (letter,i,ar) => {
if (!trie[letter]) return;
let curWord = curString + letter;
let newTrie = trie[letter];
let newLetters = [...ar.slice(0,i),...ar.slice(i+1)];
if (trie[letter][FLAG_INDEX]) words.push(curWord);
if (self.isValidPrefix(curWord, dictionary)) words = [...words,...self.findAllWords(newLetters,newTrie,curWord)];
});
return uniq(words);
}
下面是我在Go中复制它的尝试(使用trie实现):
我很想知道为什么会失败,我如何才能让它工作,以及我滥用/没有利用的任何惯例。谢谢 这可能是Go代码的唯一问题,也可能不是,但是for循环中的return语句与javascript forEach中的return语句做的事情不同 javascript代码中匿名函数内的返回从匿名函数返回到
findAllWords
函数内。从FindAllWords
返回的Go-for循环中的Return。当遇到不在trie根中的字母时,这将过早地停止操作。我假定您遇到的问题是返回的[]字符串是空的或不完整的
除了返回单词,您应该使用。这可能是Go代码的唯一问题,也可能不是,但是for循环中的return语句与javascript forEach中的return语句做的事情不同
javascript代码中匿名函数内的返回从匿名函数返回到findAllWords
函数内。从FindAllWords
返回的Go-for循环中的Return。当遇到不在trie根中的字母时,这将过早地停止操作。我假定您遇到的问题是返回的[]字符串是空的或不完整的
您应该使用而不是返回单词。好的,OP的实现中有两个问题:
func FindAllWords(letters []rune, node *Node, curString string) []string {
words := []string{}
for i, let := range letters {
n, ok := node.Children()[let]
if !ok {
return words
}
curWord := curString + string(n.val)
newLetters := []rune{}
newLetters = append(newLetters, letters[:i]...)
newLetters = append(newLetters, letters[i+1:]...)
if n.term {
words = append(words, curWord)
}
words = append(words, FindAllWords(newLetters, n, curWord)...)
}
return words
}
- if块检查子级是否存在的操作应该“继续”循环,而不是作为结果返回单词(尝试其他树分支)
- 检查当前节点是否为终端(word)的if块条件必须更改,以适应github.com/derekparker/trie作者的设计决策,即将终端节点存储为对应于单词最后一个字母的节点的子节点,并将其父节点键入为0符文
以下是工作版本:
func FindAllWords(letters []rune, node *trie.Node, curString string) []string {
words := []string{}
for i, let := range letters {
n, ok := node.Children()[let]
if !ok {
continue
}
curWord := curString + string(n.Val())
newLetters := []rune{}
newLetters = append(newLetters, letters[:i]...)
newLetters = append(newLetters, letters[i+1:]...)
if n.Children()[0x0] != nil {
words = append(words, curWord)
}
words = append(words, FindAllWords(newLetters, n, curWord)...)
}
return words
}
这里有一个更具可读性的版本(当然是我的口味):
第二个问题当然来自于依赖一个外部库,该库带有一个有点泄漏的API,该API向客户机代码公开了实现细节
为了避免这种简单情况下的麻烦,我建议构建一个简单的trie实现,它可以像下面这样简单:
type Node struct {
Char rune
Term bool
Children map[rune]*Node
}
func (n *Node) Add(s []rune) {
if len(s) == 0 {
n.Term = true
return
}
r := s[0]
c, ok := n.Children[r]
if !ok {
c = &Node{Char: r, Children: make(map[rune]*Node)}
n.Children[r] = c
}
c.Add(s[1:])
}
func Empty() *Node {
return &Node{Children: make(map[rune]*Node)}
}
使用这种结构,我加载了一个英文单词列表:
func English() *Node {
f, err := os.Open("/usr/share/dict/american-english")
if err != nil {
panic(err)
}
defer f.Close()
t := Empty()
s := bufio.NewScanner(f)
for s.Scan() {
t.Add([]rune(strings.ToLower(s.Text())))
}
return t
}
该结构可以在相同的算法中使用,只需很少的修改,并且没有实现错误:
func FindAllWords3(s string, n *Node, w string) []string {
r := []string{}
for i, l := range s {
n1, ok := n.Children[l]
if !ok {
continue
}
s1 := s[:i] + s[i+1:]
w1 := w + string(l)
if n1.Term {
r = append(r, w1)
}
r = append(r, FindAllWords3(s1, n1, w1)...)
}
return r
}
以下是应用于单词“dances”和上面加载的相同英语单词列表的上述三种实现的结果:
[d dan dance dances dane danes dean deans den dena dens dec a ad aden ads an and andes ac acne ace aced aces as ascend n nd na ne ned c cd ca cad cads can cane caned canes cans case cased cs e ed edna end ends es s sad sade san sand sane sac sn snead sc scad scan se sedan sedna sea sean sen send sec]
[d dan dance dances dane danes dean deans den dena dens dec a ad aden ads an and andes ac acne ace aced aces as ascend n nd na ne ned c cd ca cad cads can cane caned canes cans case cased cs e ed edna end ends es s sad sade san sand sane sac sn snead sc scad scan se sedan sedna sea sean sen send sec]
[d dan dance dances dane danes dean deans den dena dens dec a ad aden ads an and andes ac acne ace aced aces as ascend n nd na ne ned c cd ca cad cads can cane caned canes cans case cased cs e ed edna end ends es s sad sade san sand sane sac sn snead sc scad scan se sedan sedna sea sean sen send sec]
好的,OP的实现中有两个问题:
func FindAllWords(letters []rune, node *Node, curString string) []string {
words := []string{}
for i, let := range letters {
n, ok := node.Children()[let]
if !ok {
return words
}
curWord := curString + string(n.val)
newLetters := []rune{}
newLetters = append(newLetters, letters[:i]...)
newLetters = append(newLetters, letters[i+1:]...)
if n.term {
words = append(words, curWord)
}
words = append(words, FindAllWords(newLetters, n, curWord)...)
}
return words
}
- if块检查子级是否存在的操作应该“继续”循环,而不是作为结果返回单词(尝试其他树分支)
- 检查当前节点是否为终端(word)的if块条件必须更改,以适应github.com/derekparker/trie作者的设计决策,即将终端节点存储为对应于单词最后一个字母的节点的子节点,并将其父节点键入为0符文
以下是工作版本:
func FindAllWords(letters []rune, node *trie.Node, curString string) []string {
words := []string{}
for i, let := range letters {
n, ok := node.Children()[let]
if !ok {
continue
}
curWord := curString + string(n.Val())
newLetters := []rune{}
newLetters = append(newLetters, letters[:i]...)
newLetters = append(newLetters, letters[i+1:]...)
if n.Children()[0x0] != nil {
words = append(words, curWord)
}
words = append(words, FindAllWords(newLetters, n, curWord)...)
}
return words
}
这里有一个更具可读性的版本(当然是我的口味):
第二个问题当然来自于依赖一个外部库,该库带有一个有点泄漏的API,该API向客户机代码公开了实现细节
为了避免这种简单情况下的麻烦,我建议构建一个简单的trie实现,它可以像下面这样简单:
type Node struct {
Char rune
Term bool
Children map[rune]*Node
}
func (n *Node) Add(s []rune) {
if len(s) == 0 {
n.Term = true
return
}
r := s[0]
c, ok := n.Children[r]
if !ok {
c = &Node{Char: r, Children: make(map[rune]*Node)}
n.Children[r] = c
}
c.Add(s[1:])
}
func Empty() *Node {
return &Node{Children: make(map[rune]*Node)}
}
使用这种结构,我加载了一个英文单词列表:
func English() *Node {
f, err := os.Open("/usr/share/dict/american-english")
if err != nil {
panic(err)
}
defer f.Close()
t := Empty()
s := bufio.NewScanner(f)
for s.Scan() {
t.Add([]rune(strings.ToLower(s.Text())))
}
return t
}
该结构可以在相同的算法中使用,只需很少的修改,并且没有实现错误:
func FindAllWords3(s string, n *Node, w string) []string {
r := []string{}
for i, l := range s {
n1, ok := n.Children[l]
if !ok {
continue
}
s1 := s[:i] + s[i+1:]
w1 := w + string(l)
if n1.Term {
r = append(r, w1)
}
r = append(r, FindAllWords3(s1, n1, w1)...)
}
return r
}
以下是应用于单词“dances”和上面加载的相同英语单词列表的上述三种实现的结果:
[d dan dance dances dane danes dean deans den dena dens dec a ad aden ads an and andes ac acne ace aced aces as ascend n nd na ne ned c cd ca cad cads can cane caned canes cans case cased cs e ed edna end ends es s sad sade san sand sane sac sn snead sc scad scan se sedan sedna sea sean sen send sec]
[d dan dance dances dane danes dean deans den dena dens dec a ad aden ads an and andes ac acne ace aced aces as ascend n nd na ne ned c cd ca cad cads can cane caned canes cans case cased cs e ed edna end ends es s sad sade san sand sane sac sn snead sc scad scan se sedan sedna sea sean sen send sec]
[d dan dance dances dane danes dean deans den dena dens dec a ad aden ads an and andes ac acne ace aced aces as ascend n nd na ne ned c cd ca cad cads can cane caned canes cans case cased cs e ed edna end ends es s sad sade san sand sane sac sn snead sc scad scan se sedan sedna sea sean sen send sec]
当您尝试使用该代码时会发生什么?它是否有错误或给出不正确的结果?错误是什么?我只是得到一个返回的空切片。正如jdd0所指出的,我非常确定第8行需要中断而不是返回,但这并不能解决问题。我认为您不想返回或中断,如果不正常,您希望继续
(所以跳过这个例子,继续循环。相关:当您尝试使用该代码时会发生什么?它是否有错误或给出不正确的结果?错误是什么?我只是得到一个返回的空片段。我非常确定第8行需要中断,而不是返回,正如jdd0所指出的那样,但这并不能解决问题。我认为您不希望这样做。)t要返回或中断,如果不正常,您希望继续
(因此跳过该案例并继续循环)。相关:您完全正确,但我仍然返回一个空片段。以下是一些输出(字母、n.val和curString),打印在函数开头,将返回更改为中断:[100 97 110 99 101 115][97 110 99 101 115]d[110 99 101 115]a da[99 101 115]n dan[101 115]c dance[115]e dance[115]e dance[110 101 115]c dance[110 101 115]c dac似乎n.term的评价永远不会正确,即使我能成功