Up to Main Index Up to Journal for September, 2022 JOURNAL FOR FRIDAY 30TH SEPTEMBER, 2022 ______________________________________________________________________________ SUBJECT: Exploring maps DATE: Fri 30 Sep 20:32:31 BST 2022 It’s been a little quiet here as I’ve not had much time for coding or writing. The time I did have, since releasing Mere v0.0.4[1], was spent implementing maps. This post took way longer to write than usual, as I only wanted to show actual working code for the examples and I kept breaking things. I also went and changed the syntax for maps half way through writing this… The biggest stumbling block has been the fact that in Go you cannot take the address of a map element. Otherwise, I’d have used the same technique I used for slices which worked really well. It would have made things much simpler. Anyway, after many iterations, I nearly have maps working. In Mere, arrays are defined as ‘[]type’ where ‘type’ is the type of the array entry, indexes are always of type int. Maps are defined as ‘[type]’ where ‘type’ is the type of the map index and map entries can be of any type. Some examples: inv:[string] = [string] "item" "Apple" "qty" 25 "price" 0.25 println "inv: ", inv println printf "Item name:\t%s\t%s\n", inv["item"], type inv["item"] printf "Quantity:\t%d\t%s\n", inv["qty"], type inv["qty"] printf "Price:\t\t%4.2f\t%s\n", inv["price"], type inv["price"] Here ‘inv’ is a map with string index values. It has 3 entries: item, qty and price. When run the code produces: inv: map[item:Apple price:0.25 qty:25] Item name: Apple string Quantity: 25 int Price: 0.25 float A new built-in ‘keys’ has been added to retrieve all the map indexes. For example to iterate over a map: inv:[string] = [string] "item" "Apple" "qty" 25 "price" 0.25 k:[]string = keys inv kloop: println k[0] ":\t" inv[k[0]] delete k 0 if len(k) > 0; goto kloop When run, the code produces: item: Apple price: 0.25 qty: 25 As maps can contain any type, maps can contain maps and slices. In the next example an alternative way to create and populate a map is used, this time each entry is a map with string indexes. The commas in the maps for each entry are used to highlight the map key/value pairs, but are not required: fruits:[string] fruits["apple"] = [string] "item" "Apple", "qty" 25, "price" 0.25 fruits["orange"] = [string] "item" "Orange", "qty" 37, "price" 0.30 fruits["mango"] = [string] "item" "Mango", "qty" 101, "price" 1.25 // Some temporary work variables fruit:[string]; total:float; cost:float line:string = "% 6s: %3d × %4.2f = %6.2f\n" k:[]string = keys fruits kloop: fruit = fruits[k[0]] total += cost = float fruit["qty"] * fruit["price"] printf line fruit["item"], fruit["qty"], fruit["price"], cost delete k 0 if len(k) > 0; goto kloop printf "%27s\n" "======" printf "%19s: %6.2f\n" "Total" total Yes, I really did code “total += cost = float fruit…” ;) When run, the code produces the following output: Apple: 25 × 0.25 = 6.25 Mango: 101 × 1.25 = 126.25 Orange: 37 × 0.30 = 11.10 ====== Total: 143.60 As can be seen from above, Indexing into maps with nested maps, slices or strings works as expected. For each map type it is possible to create a map from a slice of the same type, each pair of slice elements corresponding to a key/value map pair: println [int] []int 1 1 2 10 3 11 println [float] []float 1.1 1.0 2.2 2.0 println [string] []string "a" "A" "b" "B" println [bool] []bool false true true false Would produce: map[1:1 2:10 3:11] map[1.1:1 2.2:2] map[a:A b:B] map[false:true true:false] While the instance of using a string slice is handy, the others were added for completeness. However, I’m not sure if this feature is really useful — I might drop it before the next release. Similar to slices, maps in Mere can be concatenated together and multiple entries appended — to nitpick here, in the case of maps, entries are actually added and not appended but the similarity works. For example: f1:[string] = [string] "apple" [string] "qty" 25, "price" 0.25 f2:[string] = [string] "orange" [string] "qty" 37, "price" 0.30 f3:[string] = [string] f1, f2 f3:[string] = [string] f3, ("mango" [string] "qty" 101, "price" 1.25), ("banana" [string] "qty" 78, "price" 0.97) println f3 Here f1 and f2 are maps with string indexes. We then create f3 and populate it by concatenating f1 and f2 together. Then we add two new entries to f3. The final output produced, manually line-wrapped for clarity, is: map[ apple:map[price:0.25 qty:25] banana:map[price:0.97 qty:78] mango:map[price:1.25 qty:101] orange:map[price:0.3 qty:37] ] Note the second [string] in the first four lines of code create an anonymous map that is then allocated to f1, f2 and f3. On the fourth line this means f3 is allocated to the anonymous map, then the entries are added and the new map allocated back to f3. For large maps this technique is less efficient than using f3["mango"] and f3["banana"] to add the map entries. Something I will have to look at optimizing. I have all of the above examples working. There are a few iffy edge cases I’m sorting out. I then need to update all of the operators and built-ins to handle maps and map references where applicable — so far I’ve only updated a few to prove my map concept implementation will work. After that I’ll push out a Mere v0.0.5 for people to play with. I’d like to take a minute to thank everybody who has taken the time to play with Mere and to provide comments and feedback. It’s much appreciated. If you have any further thoughts, maybe on the maps, maybe not: diddymus@wolfmud.org -- Diddymus [1] ../../../annex Up to Main Index Up to Journal for September, 2022