I was browsing some code on Rosetta and found this for combining two arrays with keys/values into a hash table:
import tables, sequtils
proc asKeyVal(x): auto = cast[seq[tuple[key: char, val: int]]](x)
let keys = @['a','b','c']
let values = @[1, 2, 3]
let table = toTable zip(keys, values).asKeyVal
Which looked "ugly" because of that strange cast. Well not everything can look good. But the compiler also complained.
I changed it to:
import tables, sequtils
proc asKeyVal[T](x: T): auto = cast[seq[tuple[key: char, val: int]]](x)
let keys = @['a','b','c']
let values = @[1, 2, 3]
let table = toTable zip(keys, values).asKeyVal
echo table
Which compiles. Here my questions:
I think I wrote that code. The problem/reason is that you can't just change the names inside a tuple, or make the compiler ignore them, so they have to be converted explicitly. I'd like to know a better way to do this as well.
About the compiler complaining about typeless parameters: I really liked them, reminds of Haskell and hides types, which makes it look closer to Python.
OK. Forget about that stuff I asked in 3 as I just saw that it calls "toTable" anyway.
But why not just doing this:
import tables, sequtils
let keys = @['a','b','c']
let values = @[1, 2, 3]
#let table = newTable[char, int]()
# better?
let table = newTable[char, int](keys.high)
for t in zip(keys, values):
table[t.a] = t.b
echo table
Instead of casting and doing the same with "toTable()" later on?
Is the code from Rosetta more efficient in a way I don't see?
Then I tried to do this (which looks like something I expected from the standard Library):
proc toTable[A, B](keys: A, values: B): auto =
result = newTable[A, B]()
for t in zip(keys, values):
result[t.a] = t.b
echo toTable(keys, values)
Which fails with an error at compile time I do not understand:
hash_table_1.nim(17, 12) Info: template/generic instantiation from here
hash_table_1.nim(15, 14) Error: type mismatch: got (TableRef[seq[char], seq[int]], char, int)
but expected one of:
system.[]=(s: var string, x: Slice[system.int], b: string)
system.[]=(a: var array[Idx, T], x: Slice[system.int], b: openarray[T])
system.[]=(a: var array[Idx, T], x: Slice[[]=.Idx], b: openarray[T])
system.[]=(s: var seq[T], x: Slice[system.int], b: openarray[T])
tables.[]=(t: var CountTable[[]=.A], key: A, val: int)
tables.[]=(t: CountTableRef[[]=.A], key: A, val: int)
tables.[]=(t: var OrderedTable[[]=.A, []=.B], key: A, val: B)
tables.[]=(t: var Table[[]=.A, []=.B], key: A, val: B)
tables.[]=(t: TableRef[[]=.A, []=.B], key: A, val: B)
tables.[]=(t: OrderedTableRef[[]=.A, []=.B], key: A, val: B)
I thought that should work?
I think that could be rewritten to use toTableFrom() but I did not really understood how and there is a comment about something which has to change later in the source.
BINGO: I think I just understood the problem and reason for that cast :)
The tuple names have to be key and val for "toTable" whereas zip() names them a b? Is it this?
I always think of tuples as anonymous like in Haskell or Rust, just defining an order and being heterogeneous. Well in Nim they are more like "struct" in my understanding.
Does that make any sense?
Talking mostly to myself I just created an Issue on GitHub for this:
I was too late.. but yeah: :)
import tables, sequtils
let keys = @['a','b','c']
let values = @[1, 2, 3]
proc toTable[A, B](keys: seq[A], values: seq[B]): auto =
result = newTable[A, B]()
for t in zip(keys, values):
result[t.a] = t.b
echo toTable(keys, values)
seq[A] and not A
why is newTable() returning a TableRef but initTable() a Table?
That is what Nim generally does, see for example Adam's reply at
http://stackoverflow.com/questions/22096880/what-is-the-model-of-value-vs-reference-in-nimrod
Well I think that toTable() should either (also) expect a/b as I proposed on GitHub. Or zip() could support arbitrary names. I think those are chosen by some magic in the compiler (if I remember the code right).
Another way could be to create your cast as "standard template / macro" to rename struct elements and make it some documented feature:
toTable transposeTupleNames(zip(keys,values),'key','val')
I guess something would be possible to create the cast from it?