Skip to content

Instantly share code, notes, and snippets.

@ynonp
Last active November 3, 2017 07:33
Show Gist options
  • Save ynonp/2282c5f712194c686bbcbff9b70befbb to your computer and use it in GitHub Desktop.
Save ynonp/2282c5f712194c686bbcbff9b70befbb to your computer and use it in GitHub Desktop.
Elixir Cartesian Product Macro
@doc """
cp produces cartesian product from a list of lists, calling handler on each
result. Handler is expected to receive as many arguments as there are groups.
## Examples
iex> HelloWorld.cp([["10", "20"], ["a", "b", "c"], ["xx", "yy"]], fn(x, y,z) -> {x,y,z} end)
[{"10", "a", "xx"}, {"10", "a", "yy"}, {"10", "b", "xx"}, {"10", "b", "yy"},
{"10", "c", "xx"}, {"10", "c", "yy"}, {"20", "a", "xx"}, {"20", "a", "yy"},
{"20", "b", "xx"}, {"20", "b", "yy"}, {"20", "c", "xx"}, {"20", "c", "yy"}]
"""
defmacro cp(groups, handler) do
args = (for idx <- 0..length(groups)-1, do: String.to_atom("a#{idx}"))
|> Enum.map(fn(i) -> Macro.var(i, __MODULE__) end)
ast = quote do
for i <- [] do
apply(unquote(handler), unquote(args))
end
end
Macro.prewalk(ast, fn(x) ->
case x do
{:for, [], iters} ->
[_ | t] = iters
{
:for,
[],
(
groups |>
Stream.with_index |>
Enum.map(
fn({val, idx}) -> {
:<-, [], [{String.to_atom("a#{idx}"), [], unquote(__MODULE__)}, val]
} end)
) ++ t
}
_ ->
x
end
end)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment