I am brainstorming how to create a data-structure that contains anonymous functions that are used if a value matches a pattern. The data-structure needs to change dynamically.
A map seems to fit:
%{{1,2,3} => :func_a,{2,3,4} => :func_b}[{1,2,3}]
But the key is not a pattern it would be great to be able to do so.
I was hoping that it could be specified like a case statement:
case {1, 2, 3} do
{4, 5, 6} ->
"This clause won't match"
{1, 2, x} ->
"This clause will match and bind x to 3 in this clause"
_ ->
"This clause would match any value"
end
Maps are hash tables (for 32+ elements) indexed by key. Therefore a lookup such as {1, 2, _}, where we don’t fully have to key to hash, would require a linear traversal of all keys. In other words, maps are really not a good fit for the lookups you want to perform, regardless of Elixir having syntax for it or not.
Luckily, a relatively straight-forward solution is to nest your keys:
a = fn({a,b,c}) when a==1 and b==2 -> c end
b = fn({a,b,c}) when a==2 and b==3 -> c end
table = :ets.new(:table, [:set, :protected])
ms = :ets.fun2ms(a)++:ets.fun2ms(b)
:ets.insert(table, { :ets.fun2ms(a), a})
:ets.insert(table, { :ets.fun2ms(b), b})
:erlang.match_spec_test({1,2,3}, ms, :table) # ok
:erlang.match_spec_test({2,3,10}, ms, :table) # ok
:erlang.match_spec_test({1,3,2}, ms, :table) # doesn't match
What irritates me about this solution is (1) that I have to two things to take care of, the array of match_specs and the table. And (2) I have to use a atom to refer to the table.
Am I right about this? or do use these functions in a unintended way?