Recursion in Go - recursion

I'm a Javascript developer by trade and decided to give Go a spin. As a learning exercise I decided to port a function in one of my node projects, but can't get it working for the life of me. The function's purpose is to display all of the valid english words that can be made from the letters present in a different word (I'm building a multiplayer version of Text Twist). For example, findAllWords("dances") would return ['can','scan','dance','dances',etc...]. I've achieved this by recursing on a trie built from an English word list.
Here is the function's implementation in Javascript:
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);
}
and here's my attempt at replicating it in Go (using this trie implementation):
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
}
Would love to know why this is failing, how I can get it working, and any conventions I'm abusing/not making use of. Thanks!

This may or may not be the only problem with the Go code, but the return statement in the for loop is not doing the same thing as the return statement in the javascript forEach.
Return within the anonymous function in the javascript code returns from the anonymous function to within the findAllWords function. Return within the Go for loop returns from FindAllWords. This will prematurely stop the operation when it comes across a letter not within the root of the trie. I presume the issue you are having is the []string being returned is empty or incomplete.
Instead of return words, you should use break.

OK, there are two problems in OP's implementation:
The action of the if block checking for child existence should "continue" the loop instead of returning words as a result (to try other tree branches).
The if block condition checking whether the current node is a terminal (word) must be changed to accomodate github.com/derekparker/trie author's design decision of storing the terminal node as a child of the node corresponding to the last letter of the word, keyed in its parent as the 0 rune.
Here's the working version:
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
}
Here's a somewhat more readable version (to my taste, of course):
func FindAllWords2(s string, n *trie.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.Children()[0x0] != nil {
r = append(r, w1)
}
r = append(r, FindAllWords2(s1, n1, w1)...)
}
return r
}
The second problem is of course coming from depending on an external library with a somewhat leaky API which exposes implementation details to the client code.
To avoid such kind of trouble for this simple case, I would recommend to build a simple trie implementation which can come as easy as this:
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)}
}
Using that structure this is how I loaded an english wordlist:
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
}
That structure can be used in the same algorithm with very little modification and no implementation misteries:
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
}
Here are the results of the three implementations above applied to the word "dances" and the same english wordlist loaded above:
[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]

Related

How to randomly split a map in Go as evenly as possible?

I have a quick question. I am fairly new to golang. Say I have a map like so:
map[int]string
How could I randomly split it into two maps or arrays and as close to even as possible? So for example, if there are 15 items, it would be split 7 - 8.
For example:
func split(m map[int]string) (odds map[int]string, evens map[int]string) {
n := 1
odds = make(map[int]string)
evens = make(map[int]string)
for key, value := range m {
if n % 2 == 0 {
evens[key] = value
} else {
odds[key] = value
}
n++
}
return odds, evens
}
It is actually an interesting example, because it shows a few aspects of Go that are not obvious for beginners:
range m iterates in a random order, unlike in any other language as far as I know,
the modulo operator % returns the remainder of the integer division,
a function can return several values.
You could do something like this:
myStrings := make(map[int]string)
// Values are added to myStrings
myStrings2 := make(map[int]string)
// Seed system time for random numbers
rand.Seed(time.Now().UTC().UnixNano())
for k, v := range myStrings {
if rand.Float32() < 0.5 {
myStrings2[k] = v
delete(myStrings, k)
}
}
https://play.golang.org/p/6OnH1k4FMu

Pattern matching on two records with the same fields

Say I have this record:
type alias Rec = { a : Int }
And, for example, a function that takes two of these and sums their integers.
f: Rec -> Rec -> Int
This can be implemented using record accessors (i.e. f x y = x.a + y.a), but is there a way to use pattern matching to extract both integers?
Obviously, these two do not work because they would be binding two different numbers to the same variable:
f {a} {a} = a + a
f x y = case (x, y) of ({a}, {a}) -> a + a
There seems to be no such way in the current Elm language. In other functional languages such as ML and Haskell, you could write patterns inside records like:
$ sml
Standard ML of New Jersey v110.74 [built: Sat Oct 6 00:59:36 2012]
- fun func {field=x} {field=y} = x+y ;
val func = fn : {field:int} -> {field:int} -> int
- func {field=123} {field=45} ;
val it = 168 : int
You might as well make a feature request to the developer(s) of Elm - or ask a question in the community mailing list at least.
P.S. After a quick search, I found such a proposal to add ML-like pattern matching on record fields in Elm, but it seems to have been turned down.:-(
There's no way to do this currently. There is pattern aliasing (as) but it only works for a whole pattern, so this is invalid:
type alias Rec = { a : Int }
f: Rec -> Rec -> Int
f { a as xa } { a as ya } = xa + ya
main = f { a = 1 } { a = 2 }
results in:
Detected errors in 1 module.
-- SYNTAX PROBLEM --------------------------------------------------------------
I ran into something unexpected when parsing your code!
4| f { a as xa } { a as ya } = xa + ya
^
I am looking for one of the following things:
a closing bracket '}'
whitespace

Very Confusing variable changes

http://play.golang.org/p/Vd3meom5VF
I have this code for some context free grammar in Go
And I am looking at this code so many times and still don't see any reason for the struct values to be changed. Could anybody see why the change like the following happens?
Rules:
S -> . [DP VP]
VP -> . [V DP]
VP -> . [V DP AdvP]
After I run some functions as in the line
or2 = append(or2, OstarCF([]QRS{q}, []string{"sees"}, g2.Nullables(), g2.ChainsTo(g2.Nullables()))...)
Somehow my struct value is changed... I don't know why...
Rules:
S -> . [VP VP]
VP -> . [DP DP]
VP -> . [AdvP AdvP AdvP]
This should have been same as above.
Rules:
S -> DP,VP
VP -> V,DP
VP -> V,DP,AdvP
or2 := []QRS{}
g2 := ToGrammar(cfg2)
fmt.Printf("%s\n", g2)
for _, rule := range g2.Rules {
q := QRS{
one: rule.Src,
two: []string{},
three: rule.Right,
}
or2 = append(or2, OstarCF([]QRS{q}, []string{"sees"}, g2.Nullables(), g2.ChainsTo(g2.Nullables()))...)
}
fmt.Printf("%s\n", g2)
As you see, I do not use any pointer the variable rule, and they are only used to instantiate another struct value, but how come the original struct field rule has changed? The function OstarCF does not do anything about this field rule
func OstarCF(Qs []QRS, R []string, nD map[string]bool, cD map[string][]string) []QRS {
symbols := []string{}
for _, r := range R {
symbols = append(symbols, cD[r]...)
}
product := []QRS{}
for _, Q := range Qs {
a := Q.one
b := Q.two
c := Q.three
if len(c) > 0 && CheckStr(c[0], symbols) {
b = append(b, c[0])
np := QRS{
one: a,
two: b,
three: c[1:],
}
product = append(product, np)
for len(np.three) > 0 && nD[np.three[0]] == true {
np.two = append(np.two, np.three[0])
np = QRS{
one: np.one,
two: np.two,
three: np.three[1:],
}
product = append(product, np)
}
}
}
return product
}
The original Rules field changes because pointers and slices (which are references as well) are used.
Before calling OstarCF, the ChainsTo method is called. It uses the grammar object by value, so a copy is done, but the Rules field is a slice of pointers on Rules. So when this field is copied, it still points to the data of the original object.
Then, in method ChainsTo, there is a loop on the Rules field. It copies the Right field which is a slice of strings (so it still points to data of the original object):
rhs := rule.Right
Finally, a ns variable is declared by slicing rhs:
ns := rhs[:i]
ns = append(ns, rhs[i+1:]...)
At this stage, the ns variable still points to the buffer containing the slice of strings of the original object. Initially, i=0, so ns is an empty slice reusing the buffer. When items are appended, they replace the original data.
That's why your data are changed.
You can fix this problem by explicitly making a copy, for instance by replacing the above lines by:
ns := make( []string, 0, len(rhs) )
ns = append( ns, rhs[:i]...)
ns = append( ns, rhs[i+1:]...)
Go slices have replaced C pointer arithmetic, but they can be almost as dangerous/misleading in some cases.

In Go how to get a slice of values from a map?

If I have a map m is there a better way of getting a slice of the values v than this?
package main
import (
"fmt"
)
func main() {
m := make(map[int]string)
m[1] = "a"
m[2] = "b"
m[3] = "c"
m[4] = "d"
// Can this be done better?
v := make([]string, len(m), len(m))
idx := 0
for _, value := range m {
v[idx] = value
idx++
}
fmt.Println(v)
}
Is there a built-in feature of a map? Is there a function in a Go package, or is this the only way to do this?
As an addition to jimt's post:
You may also use append rather than explicitly assigning the values to their indices:
m := make(map[int]string)
m[1] = "a"
m[2] = "b"
m[3] = "c"
m[4] = "d"
v := make([]string, 0, len(m))
for _, value := range m {
v = append(v, value)
}
Note that the length is zero (no elements present yet) but the capacity (allocated space) is initialized with the number of elements of m. This is done so append does not need to allocate memory each time the capacity of the slice v runs out.
You could also make the slice without the capacity value and let append allocate the memory for itself.
Unfortunately, no. There is no builtin way to do this.
As a side note, you can omit the capacity argument in your slice creation:
v := make([]string, len(m))
The capacity is implied to be the same as the length here.
Go 1.18
You can use maps.Values from the golang.org/x/exp package.
Values returns the values of the map m. The values will be in an indeterminate order.
func main() {
m := map[int]string{1: "a", 2: "b", 3: "c", 4: "d"}
v := maps.Values(m)
fmt.Println(v)
}
The package exp includes experimental code. The signatures may or may not change in the future, and may or may not be promoted to the standard library.
If you don't want to depend on an experimental package, you can easily implement it yourself. In fact, this code is a copy-paste from the exp package:
func Values[M ~map[K]V, K comparable, V any](m M) []V {
r := make([]V, 0, len(m))
for _, v := range m {
r = append(r, v)
}
return r
}
Not necessarily better, but the cleaner way to do this is by defining both the Slice LENGTH and CAPACITY like txs := make([]Tx, 0, len(txMap))
// Defines the Slice capacity to match the Map elements count
txs := make([]Tx, 0, len(txMap))
for _, tx := range txMap {
txs = append(txs, tx)
}
Full example:
package main
import (
"github.com/davecgh/go-spew/spew"
)
type Tx struct {
from string
to string
value uint64
}
func main() {
// Extra touch pre-defining the Map length to avoid reallocation
txMap := make(map[string]Tx, 3)
txMap["tx1"] = Tx{"andrej", "babayaga", 10}
txMap["tx2"] = Tx{"andrej", "babayaga", 20}
txMap["tx3"] = Tx{"andrej", "babayaga", 30}
txSlice := getTXsAsSlice(txMap)
spew.Dump(txSlice)
}
func getTXsAsSlice(txMap map[string]Tx) []Tx {
// Defines the Slice capacity to match the Map elements count
txs := make([]Tx, 0, len(txMap))
for _, tx := range txMap {
txs = append(txs, tx)
}
return txs
}
Simple solution but a lot of gotchas. Read this blog post for more details: https://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas
As far as I'm currently aware, go doesn't have a way method for concatenation of strings/bytes in to a resulting string without making at least /two/ copies.
You currently have to grow a []byte since all string values are const, THEN you have to use the string builtin to have the language create a 'blessed' string object, which it will copy the buffer for since something somewhere could have a reference to the address backing the []byte.
If a []byte is suitable then you can gain a very slight lead over the bytes.Join function by making one allocation and doing the copy calls your self.
package main
import (
"fmt"
)
func main() {
m := make(map[int]string)
m[1] = "a" ; m[2] = "b" ; m[3] = "c" ; m[4] = "d"
ip := 0
/* If the elements of m are not all of fixed length you must use a method like this;
* in that case also consider:
* bytes.Join() and/or
* strings.Join()
* They are likely preferable for maintainability over small performance change.
for _, v := range m {
ip += len(v)
}
*/
ip = len(m) * 1 // length of elements in m
r := make([]byte, ip, ip)
ip = 0
for _, v := range m {
ip += copy(r[ip:], v)
}
// r (return value) is currently a []byte, it mostly differs from 'string'
// in that it can be grown and has a different default fmt method.
fmt.Printf("%s\n", r)
}
As of 1.18, this is the best way:
https://stackoverflow.com/a/71635953/130427
Pre 1.18
You can use this maps package:
go get https://github.com/drgrib/maps
Then all you have to call is
values := maps.GetValuesIntString(m)
It's type-safe for that common map combination. You can generate other type-safe functions for any other type of map using the mapper tool in the same package.
Full disclosure: I am the creator of this package. I created it because I found myself rewriting these functions for map repeatedly.

golang map prints out of order

Why is the map printing out of order, and how do I get it in to order?
package main
import (
"fmt"
)
type monthsType struct {
no int
text string
}
var months = map[int]string{
1:"January", 2:"Fabruary", 3:"March", 4:"April", 5:"May", 6:"June",
7:"July", 8:"August", 9:"September", 10:"October", 11:"Novenber", 12:"December",
}
func main(){
for no, month := range months {
fmt.Print(no)
fmt.Println("-" + month)
}
}
Prints out:
10-October
7-July
1-January
9-September
4-April
5-May
2-Fabruary
12-December
11-Novenber
6-June
8-August
3-March
Code:
func DemoSortMap() (int, error) {
fmt.Println("use an array to access items by number:")
am := [2]string{"jan", "feb"}
for i, n := range am {
fmt.Printf("%2d: %s\n", i, n)
}
fmt.Println("maps are non-sorted:")
mm := map[int]string{2: "feb", 1: "jan"}
for i, n := range mm {
fmt.Printf("%2d: %s\n", i, n)
}
fmt.Println("access items via sorted list of keys::")
si := make([]int, 0, len(mm))
for i := range mm {
si = append(si, i)
}
sort.Ints(si)
for _, i := range si {
fmt.Printf("%2d: %s\n", i, mm[i])
}
return 0, nil
}
(most of it stolen from M. Summerfield's book)
output:
use an array to access items by number:
0: jan
1: feb
maps are non-sorted:
2: feb
1: jan
access items via sorted list of keys::
1: jan
2: feb
Maps are not sorted so you may use a slice to sort your map. Mark Summerfield's book "Programming in Go" explains this on page 204 and is highly recommended.
This is very late answer, but from what I have read maps are unsorted by design, and are random as one should not rely on the order.
Besides using the sort package together with a second map, one can also use the fmt.Prinln(theMap), which will print the map sorted.
fmt: print maps in key-sorted order
This will print the map typically as follows:
map[key:value
key:value
key:value
]
But this might not be what you want...
By using the fmt.Sprint one can then manipulate the string if needed.
i.e.
s := fmt.Sprint(theMap)
s1 := s[4 : len(s)-1] // remove map[ and ]
fmt.Println(s1)

Resources