How do i do calculations on things that are in a tuple expression? - recursion

Hey I'm new to Elixir (just started three days ago) and I'm trying to write a program that can calculate derivatives and I'm stuck at trying to simplify the expression for ease of reading. Only done Java and C earlier.
So I have defined this at the top
defmodule Deriv do
#type literal() :: {:num, number()} | {:var, atom()}
#type expr() ::
literal() | {:add, expr(), expr()} | {:mul, expr(), expr()}
| {:exp, expr(), literal()} | {:div, literal(), expr()} |
# {:ln, literal(), expr()} | {:ln, literal(), literal()}
{:ln, expr()}
And I'm trying to simplify the expression I get from running this test
def test_exp2() do
e = {:exp, {:add, {:mul, {:num, 2}, {:var, :x}}, {:num, 3}}, {:num, 2}}
d = deriv(e, :x)
IO.write("Expression: #{p_print(e)}\n")
IO.write("Derivative of expression: #{p_print(d)}\n")
IO.write("Simplified: #{p_print(simplify(d))}\n")
:ok
end
So I have these function already that simplifies the expression when we do multiplications with 0 and 1, shown below
def simplify({:mul, e1, e2}) do
simplify_mul(simplify(e1), simplify(e2))
end
def simplify_mul({:num, 0}, _) do {:num, 0} end
def simplify_mul(_, {:num, 0}) do {:num, 0} end
def simplify_mul({:num, 1}, e2) do e2 end
def simplify_mul(e1, {:num, 1}) do e1 end
def simplify_mul({:num, n1}, {:num, n2}) do {:num, n1 * n2} end
But I can't get a function that does the multiplication thing mention above to work. The problem is that I don't know the syntax to use.
The output from running this is 2*(2*x + 3)*2 but I would like it to be (8*x + 12)
So I want some kind of function like simplify_mul({:num, n1}, e2) where I multiply the number n1 with everything in the expression e2. I've tried things like
def simplify_mul({:num, n1}, {:mul, {:num, mulnum1}, e2}) do
{:mul, {:num, n1*mulnum1}, e2}
end
but it didn't work. Anyone know how one would go about doing this?
Edit:The code minus some test functions
defmodule Deriv do
#type literal() :: {:num, number()} | {:var, atom()}
#type expr() ::
literal() | {:add, expr(), expr()} | {:mul, expr(), expr()}
| {:exp, expr(), literal()} | {:div, literal(), expr()} |
# {:ln, literal(), expr()} | {:ln, literal(), literal()}
{:ln, expr()}
def test_exp2() do
e = {:exp, {:add, {:mul, {:num, 2}, {:var, :x}}, {:num, 3}}, {:num, 2}}
d = deriv(e, :x)
IO.write("Expression: #{p_print(e)}\n")
IO.write("Derivative of expression: #{p_print(d)}\n")
IO.write("Simplified: #{p_print(simplify(d))}\n")
:ok
end
def test_ln() do
e =
{:mul, {:num, 2}, {:ln, {:exp, {:add, {:mul, {:num, 2}, {:var, :x}}, {:num, 3}}, {:num, 2}}}}
d = deriv(e, :x)
IO.write("Expression: #{p_print(e)}\n")
IO.write("Derivative of expression: #{p_print(d)}\n")
IO.write("Simplified: #{p_print(simplify(d))}\n")
:ok
end
###### Our derivatives rules #######
# derivative of a constant
def deriv({:num, _}, _) do {:num, 0} end
# derivative of x to the power of one
def deriv({:var, v}, v) do {:num, 1} end
# derivative of another variable than x
def deriv({:var, _}, _) do {:num, 0} end
# d/dx(f+g) = f'(x) + g'(x)
def deriv({:add, e1, e2}, v) do {:add, deriv(e1, v), deriv(e2, v)} end
# d/dx(f*g) = f'(x)g(x) + f(x)g'(x)
def deriv({:mul, e1, e2}, v) do
{:add, {:mul, deriv(e1, v), e2}, {:mul, e1, deriv(e2, v)}}
end
# d/dx(u(x)^n) = n(u(x))^(n-1)*u'(x), where n is a real number
def deriv({:exp, u, {:num, n}}, v) do
{:mul, {:mul, {:num, n}, {:exp, u, {:num, n - 1}}}, deriv(u, v)}
end
#d/dx(k/(u(x)^n)) = -nk*u'(x)/(u(x)^(n+1))
def deriv({:div, {:num, k}, {:exp, e, {:num, n}}}, v) do
{:div,
{:mul,
{:mul, {:num, k}, {:num, -n}},
deriv(e, v)
},
{:exp, e, {:num, n + 1}}
}
end
def deriv({:ln, e}, v) do {:div, deriv(e, v), e} end
# d/dx(k*ln(u(x)^n)) = kn*u'(x)/u(x)
def deriv({:mul, {:num, k}, {:exp, e, {:num, n}}}, v) do
{:div,
{:mul,
{:mul, {:num, k}, {:num, n}},
deriv(e, v)
},
{:exp, e, {:num, n}}
}
end
###### --------------------- #######
#simplifies the expression by removing zeros and ones etc.
def simplify({:add, e1, e2}) do
simplify_add(simplify(e1), simplify(e2))
end
def simplify({:mul, e1, e2}) do
simplify_mul(simplify(e1), simplify(e2))
end
def simplify({:exp, e1, e2}) do
simplify_exp(simplify(e1), simplify(e2))
end
def simplify({:div, e1, e2}) do
simplify_div(simplify(e1), simplify(e2))
end
def simplify({:ln, e}) do simplify_ln(simplify(e)) end
def simplify(e) do e end
def simplify_add({:num, 0}, e2) do e2 end
def simplify_add(e1, {:num, 0}) do e1 end
def simplify_add({:num, n1}, {:num, n2}) do {:num, n1 + n2} end
def simplify_add(e1, e2) do {:add, e1, e2} end
def simplify_mul({:num, 0}, _) do {:num, 0} end
def simplify_mul(_, {:num, 0}) do {:num, 0} end
def simplify_mul({:num, 1}, e2) do e2 end
def simplify_mul(e1, {:num, 1}) do e1 end
def simplify_mul({:num, n1}, {:num, n2}) do {:num, n1 * n2} end
def simplify_mul({:num, n1}, {:mul, {:num, mulnum1}, e2}) do
simplify({:mul, {:num, n1*mulnum1}, e2})
end
def simplify_mul(e1, e2) do {:mul, e1, e2} end
def simplify_exp(_, {:num, 0}) do {:num, 1} end
def simplify_exp(e1, {:num, 1}) do e1 end
def simplify_exp({:num, n1}, {:num, n2}) do {:num, :math.pow(n1, n2)} end
def simplify_exp(e1, e2) do {:exp, e1, e2} end
def simplify_div({:num, 0}, _) do {:num, 0} end
def simplify_div(e1, e2) do {:div, e1, e2} end
def simplify_ln({:num, 1}) do {:num, 0} end
def simplify_ln({:num, 0}) do {:num, 0} end
def simplify_ln(e) do {:ln, e} end
# p_print functions converts from our syntax tree into strings for ease of reading
def p_print({:num, n}) do "#{n}" end
def p_print({:var, v}) do "#{v}" end
def p_print({:add, e1, e2}) do "(#{p_print(e1)} + #{p_print(e2)})" end
def p_print({:mul, e1, e2}) do "#{p_print(e1)}*#{p_print(e2)}" end
def p_print({:exp, e1, e2}) do "(#{p_print(e1)})^(#{p_print(e2)})" end
def p_print({:div, e1, e2}) do "(#{p_print(e1)}/#{p_print(e2)})" end
def p_print({:ln , e1}) do "ln(#{p_print(e1)})" end
end

I feel like this is broadly on the right track, you're just short a couple of rules.
def test do
# the input expression is 2*(2x+3)*2
e = {:add, {:mul, {:num, 2}, {:var, "x"}}, {:num, 3}}
e = {:mul, {:num, 2}, e}
e = {:mul, e, {:num, 2}}
e |> simplify() |> IO.inspect()
end
Since this is only binary expressions, I've represented this as (2 * (2x+3)) * 2.
The first thing that can help cut down the number of cases is to swap the operands if the second one is a number but the first isn't. You can then repeat the simplification step on the swapped result. You're guaranteed the first operand isn't a number (you have a separate simplification step for "both numbers") so this is guaranteed to terminate.
def simplify_mul(e1, {:num, _}=e2) do
simplify_mul(e2, e1)
end
This changes the expression to 2 * (2 * (2x+3)). Now you can specifically match the case where you're multiplying a constant by a multiplication, and the first operand of the inner multiplication is also a constant. This involves a deeper Elixir pattern match, but that's fine; you're not constrained to only matching the top-level structure.
def simplify_mul({:num, n1}, {:mul, {:num, n2}, e3}) do
{:mul, {:num, n1 * n2}, e3}
end
This reduces the expression to 4 * (2x+3). The last step is to distribute multiplication over addition; if you're multiplying anything by an addition as the second argument, turn that into an addition of multiplications. This will result in new multiplications that need to be recursively simplified; so long as you don't reverse this process, the easiest approach is to just simplify the resulting addition.
def simplify_mul(e1, {:add, e2, e3}) do
{:add, {:mul, e1, e2}, {:mul, e1, e3}} |> simplify()
end
$ elixir tmp.exs
{:add, {:mul, {:num, 8}, {:var, "x"}}, {:num, 12}}
You can add as many rules as you need to this rewriting; the only important thing to make sure of is that the rules do terminate. So for example I've written a rule that "numbers must be first", but you need to take care also pairing this with a rule that "addition must be first" since multiplying a number and an addition would result in an infinite loop.

The following should solve it, it added two extra accumulators:
p (for the product of number literals), starting at 1
and m for building a multiplication of expressions, starting at nil meaning there is no expression
It also moved from passing two factors to a list of factors: the benefit is that when you encountered a nested multiplication, you can just add its two factors to the list and keep processing, until the list is empty.
def simplify({:mul, e1, e2}) do
simplify_mul([simplify(e1), simplify(e2)], 1, nil)
end
def simplify_mul([], p, nil), do: {:num, p}
def simplify_mul([], p, m), do: {:mul, {:num, p}, m}
def simplify_mul([{:num, 0} | _], _, _), do: {:num, 0}
def simplify_mul([{:num, n1} | rest], p, m) do
simplify_mul(rest, p * n1, m)
end
# nested multiplication: add factors to the list
def simplify_mul([{:mul, e1, e2} | rest], p, m) do
simplify_mul([e1, e2 | rest], p, m)
end
def simplify_mul([other | rest], p, nil) do
simplify_mul(rest, p, other)
end
def simplify_mul([other | rest], p, m) do
simplify_mul(rest, p, {:mul, m, other})
end
iex> simplify({:mul, {:mul, {:num, 2}, {:var, :x}}, {:mul, {:var, :y}, {:num, 3}}})
{:mul, {:num, 6}, {:mul, {:var, :x}, {:var, :y}}}

Related

How do I map a value from a map to a variable when map is passed in to a function in Erlang?

I have this scenario:
Write a function eval/2 that accepts as its first argument a tuple and second argument a map which maps atoms to numbers. For instance,the call eval({add, a, b}, #{a => 1, b => 2})return 3 and the call eval({mul, {add, a, 3}, b}, #{a => 1, b => 2}) return { ok, 8}2. More generally, eval(E, L) accepts as input an expression tuple E of three elements {Op, E1, E2} where Op is add, mul, ’div’ or sub and E1 and E2 is either a number, atom or an expression tuple, and an Erlang map L that acts as lookup table for atoms. The function returns either {ok, Value } or {error, Reason}, where Reason is either variable_not_found if an atom does not exist in the lookup table or unknown_error.
Implement the function eval/2 in the module task1 and export it.
example:
1> eval({add, 1, 2}, #{}).
{ok, 3}
2> eval({add, a, b}, #{a=>1}).
{error, variable_not_found}
3> eval({add, {add, a, b}, {add, 1, 2}}, #{a=>2, b=>3}). {ok, 8}
I solved the problem when only a tuple is sent but I don't really know how to handle the map that is sent to the function.
This is my code:
-module(task1).
-export([eval/1, eval/2]).
eval_inner({add, X, Y}) ->
eval_inner(X) + eval_inner(Y);
eval_inner({mul, X, Y}) ->
eval_inner(X) * eval_inner(Y);
eval_inner({'div', X, Y}) ->
eval_inner(X) / eval_inner(Y);
eval_inner({sub, X, Y}) ->
eval_inner(X) - eval_inner(Y);
eval_inner(X) when is_number(X) ->
X;
eval_inner(X) when is_atom(X) ->
maps:get(X, M).
eval(X) ->
try eval_inner(X) of
V -> {ok, V}
catch
_:_ -> error
end.
eval(X, M) ->
you have to pass the values map around.
-module(task1).
-export([eval/1, eval/2]).
eval_inner({add, X, Y}, M) ->
eval_inner(X, M) + eval_inner(Y, M);
eval_inner({mul, X, Y}, M) ->
eval_inner(X, M) * eval_inner(Y, M);
eval_inner({'div', X, Y}) ->
eval_inner(X, M) / eval_inner(Y, M);
eval_inner({sub, X, Y}) ->
eval_inner(X, M) - eval_inner(Y, M);
eval_inner(X, _M) when is_number(X) ->
X;
eval_inner(X, M) when is_atom(X) ->
maps:get(X, M).
eval(X, M) ->
try eval_inner(X, M) of
V -> {ok, V}
catch
_:_ -> error
end.

Prolog tail recursive helper predicate for power

I am trying to make a tail recursive helper for power predicate in Prolog. So far I have this but when testing I get a message saying there is a breakpoint when trying to call helper predicate. What am I doing wrong? Ty for the help.
trpow(Base, Exp, Res) :- trpow_helper(Base, Exp, 1, Res).
trpow_helper(_, 0, Acc, Acc).
trpow_helper(Base, Exp, Acc, Res) :-
Exp > 0,
Decexp is Exp - 1,
Acc1 is Acc * Base,
Res = Acc1,
trpow_helper(Base, Decexp, Acc1, Res).
Based on your code,
trpow(Base, Exp, Res) :- trpow_helper(Base, Exp, 1, Res).
trpow_helper(_, 0, Acc, Acc):- !. /*Cut to avoid bt to second clause*/
trpow_helper(Base, Exp, Acc, Res) :-
Exp > 0,
Decexp is Exp - 1,
Acc1 is Acc * Base, /*removed Res = Acc1, not needed*/
trpow_helper(Base, Decexp, Acc1, Res).
It now works !
Using CLP such as that in swi-prolog:
:- use_module(library(clpfd)).
trpow(Base, Exp, Res) :- trpow_helper(Base, Exp, 1, Res).
trpow_helper( _, Exp, Acc, Acc):- Exp #= 0.
trpow_helper(Base, Exp, Acc, Res) :-
Exp #> 0,
Decexp #= Exp - 1,
Acc1 #= Acc * Base,
trpow_helper(Base, Decexp, Acc1, Res).
?- trpow(1, E, 1).
E = 0 ;
E = 1 .
?- trpow(2, E, 4).
E = 2 .
?- trpow(2, E, 8).
E = 3 .
?- trpow(B, 3, 8).
B = 2 .
?- trpow(B, E, 8).
B = 8,
E = 1 ;
B = 2,
E = 3 ;
...
After which, more solutions that doesn't instanciate B. In fact it loops forever.

Math Expression Simplification f#

I'm working on a mathematical expression simplifier (rather basic, no exponents, logs, roots, fractions etc) and I have it mostly working. Of the 19 tests I've used 14 of them pass. For the 5 remaining, I have written multiple other statements in my simplify function but it doesn't seem to change a thing.
Below is code (copy and paste into an interpreter and it works just fine)
//expression types
type Expression =
| X
| Y
| Const of float
| Neg of Expression
| Add of Expression * Expression
| Sub of Expression * Expression
| Mul of Expression * Expression
// formats string
let exprToString expr =
let rec recExprStr parens expr =
let lParen = if parens then "(" else ""
let rParen = if parens then ")" else ""
match expr with
| X -> "x"
| Y -> "y"
| Const n -> n.ToString()
| Neg e -> lParen + "-" + recExprStr true e + rParen
| Add (e1, e2) -> lParen + recExprStr true e1 + "+" + recExprStr true e2 + rParen
| Sub (e1, e2) -> lParen + recExprStr true e1 + "-" + recExprStr true e2 + rParen
| Mul (e1, e2) -> lParen + recExprStr true e1 + "*" + recExprStr true e2 + rParen
recExprStr false expr
//simplification function
let rec simplify expr =
match expr with
//addition
| Add(Const(ex1), Const(ex2)) -> Const(ex1 + ex2)
| Add(ex1, Const(0.)) -> ex1 |> simplify
| Add(Const(0.), ex1) -> ex1 |> simplify
| Add(Const(num), ex1) -> Add(ex1, Const(num)) |> simplify
| Add(ex1, Neg(ex2)) -> Sub(ex1, ex2) |> simplify
| Add(Neg(ex1), ex2) -> Sub(ex2, ex1) |> simplify
//subtraction
| Sub(Const(num1), Const(num2)) -> Const(num1 - num2)
| Sub(ex1, Const(0.)) -> ex1 |> simplify
| Sub(Const(0.), ex1) -> Neg(ex1) |> simplify
//multiplication
| Mul(Const(num1), Const(num2)) -> Const(num1 * num2)
| Mul(ex1, Const(1.)) -> ex1 |> simplify
| Mul(Const(1.), ex1) -> ex1 |> simplify
| Mul(ex1, Const(0.)) -> Const(0.)
| Mul(Const(0.), ex1) -> Const(0.)
| Mul(ex1, Const(num1)) -> Mul(Const(num1), ex1) |> simplify
| Mul(Neg(ex1), ex2) -> Neg(Mul(ex1, ex2)) |> simplify
| Mul(ex1, Neg(ex2)) -> Neg(Mul(ex1, ex2)) |> simplify
//negation involving a number
| Neg(Const(0.)) -> Const(0.)
| Neg(Neg(ex1)) -> ex1 |> simplify
| _ -> expr
//Tests
printfn "---Provided Tests---"
let t1 = Add (Const 5.0, Const 3.0)
let t2 = Sub (Const 5.0, Const 3.0)
let t3 = Mul (Const 5.0, Const 3.0)
let t4 = Neg (Const 4.0)
let t5 = Neg (Const -9.0)
let t6 = Add (X, Const 0.0)
let t7 = Add (Const 0.0, Y)
let t8 = Sub (X, Const 0.0)
let t9 = Sub (Const 0.0, Y)
let t10 = Sub (Y, Y)
let t11 = Mul (X, Const 0.0)
let t12 = Mul (Const 0.0, Y)
let t13 = Mul (X, Const 1.0)
let t14 = Mul (Const 1.0, Y)
let t15 = Neg (Neg X)
let t16 = Sub (Mul (Const 1.0, X), Add (X, Const 0.0))
let t17 = Add (Mul (Const 4.0, Const 3.0), Sub (Const 11.0, Const 5.0))
let t18 = Sub (Sub (Add (X, Const 1.0), Add (X, Const 1.0)), Add (Y, X))
let t19 = Sub (Const 0.0, Neg (Mul (Const 1.0, X)))
//Output goes here!
//5 + 3 = 0
printfn "t1 Correct: 8\t\tActual: %s" (exprToString (simplify t1))
//5-3 = 2
printfn "t2 Correct: 2\t\tActual: %s" (exprToString (simplify t2))
//5 * 3 = 15
printfn "t3 Correct: 15\t\tActual: %s" (exprToString (simplify t3))
//-(4) = -4
printfn "t4 Correct: -4\t\tActual: %s" (exprToString (simplify t4))
//-(-9) = 9
printfn "t5 Correct: 9\t\tActual: %s" (exprToString (simplify t5))
//x + 0 = x
printfn "t6 Correct: x\t\tActual: %s" (exprToString (simplify t6))
//0 + y = y
printfn "t7 Correct: y\t\tActual: %s" (exprToString (simplify t7))
//x - 0 = x
printfn "t8 Correct: x\t\tActual: %s" (exprToString (simplify t8))
//0 - y = -y
printfn "t9 Correct: -y\t\tActual: %s" (exprToString (simplify t9))
//y - y = 0
printfn "t10 Correct: 0\t\tActual: %s" (exprToString (simplify t10))
//x * 0 = 0
printfn "t11 Correct: 0\t\tActual: %s" (exprToString (simplify t11))
//0 * y = 0
printfn "t12 Correct: 0\t\tActual: %s" (exprToString (simplify t12))
//x * 1 = x
printfn "t13 Correct: x\t\tActual: %s" (exprToString (simplify t13))
//1 * y = y
printfn "t14 Correct: y\t\tActual: %s" (exprToString (simplify t14))
//-(-x) = x
printfn "t15 Correct: x\t\tActual: %s" (exprToString (simplify t15))
// (1 * x) - (x + 0) = 0
printfn "t16 Correct: 0\t\tActual: %s" (exprToString (simplify t16))
//(4 * 3) + (11 - 5) = 18
printfn "t17 Correct: 18\t\tActual: %s" (exprToString (simplify t17))
// ((x + 1) - (x + 1)) - (y+x) = -y -x
printfn "t18 Correct: -y -x\t\tActual: %s" (exprToString (simplify t18))
// 0 - (-(1 * x)) = x
printfn "t19 Correct: x\t\tActual: %s" (exprToString (simplify t19))
// (x + 1) * (-2y + x)
The output of the program is as follows, I have marked the 6 tests that fail (correct is the proper answer, actual is what i'm returning)
t1 Correct: 8 Actual: 8
t2 Correct: 2 Actual: 2
t3 Correct: 15 Actual: 15
t4 Correct: -4 Actual: -4
t5 Correct: 9 Actual: --9 //FAILS
t6 Correct: x Actual: x
t7 Correct: y Actual: y
t8 Correct: x Actual: x
t9 Correct: -y Actual: -y
t10 Correct: 0 Actual: y-y //FAILS
t11 Correct: 0 Actual: 0
t12 Correct: 0 Actual: 0
t13 Correct: x Actual: x
t14 Correct: y Actual: y
t15 Correct: x Actual: x
t16 Correct: 0 Actual: (1*x)-(x+0) //FAILS
t17 Correct: 18 Actual: (4*3)+(11-5) //FAILS
t18 Correct: -(y + x) Actual: ((x+1)-(x+1))-(y+x) //FAILS
t19 Correct: x Actual: x
I'm a bit perplexed as to how I might solve the final 4 (16,17,18) but It seems to me like what I have for #5 and #10 should work.
For test 5, I included | Neg(Neg(ex1)) -> ex1 |> simplify which I thought would catch my double negative but doesn't.
For test 10, I figured something like | Sub(ex1, ex2) -> (ex1 - ex2) would work, but it turns out that's not even valid syntax.
I've looked through a half dozen or so resources on simplification, and even copying and pasting some of their work my tests still fail. It know I must just be missing a case or two, but I'm pulling my hair trying to figure what I might have left out! I greatly appreciate any input!
(Original post had a test 20, I have since removed it for answer simplification purposes. Given my current code, I realized I couldn't possibly simplify it)
5: Neg(Const -9) does not get simplified. It is not a negative of a negative. You need a rule Neg(Const x) -> Const(-x) to replace the Neg(Const 0.) one.
10: Sub(x,y) when y = x -> Const(0)
16: You are not simplifying the inner parts. I would do this before simplifying the outer parts. E.g. Sub(x,y) -> let x',y' = simplify x, simplify y and then match x',y' with....
17: Solution to 16 would fix this.
18: Solution to 10 and 16 would fix this.
Also I cannot resist suggesting let t = [Add (Const 5.0, Const 3.0); Sub (Const 5.0, Const 3.0)...] and then t |> List.iteri ... .
I am using an ad hoc algebraic simplifier which does OK but would like to create a much more serious one. If anyone is seriously intersted in working on this please let me know.

How to change all the values in an Elixir map

I see that there's an update in the Dict module, but what about an update_all method that changes all values?
I tried doing this with Enum.map but the type changed:
iex(6)> Enum.map(%{:a => 2}, fn {k, v} -> {k, v + 1} end)
[a: 3]
You could pipe to Enum.into(%{}) or use a for comprehension, i.e.:
iex> for {k, v} <- %{a: 1, b: 2}, into: %{}, do: {k, v + 1}
%{a: 2, b: 3}
You can also do:
iex> Map.new(%{:a => 2}, fn {k, v} -> {k, v + 1} end)
%{:a => 3}
But feel like there should be something in the standard library to make this easier (Map.??(%{:a => 2}, &(&1 + 1))).
Here's one idea:
def update_map map, [head|tail], func do
update_map(
Dict.update(map, head, :unknown, func),
tail,
func
)
end
def update_map map, [], _ do
map
end
Then to call it:
iex(1)> d = %{:a => 1, :b => 2, :c => 3}
%{a: 1, b: 2, c: 3}
iex(2)> update_map(d, Dict.keys(d), fn v -> v + 1 end)
%{a: 2, b: 3, c: 4}
Let me add Enum.into into the mix
headers
|> Enum.group_by(fn {k, _v} -> k end, fn {_k, v} -> v end)
|> Enum.into(%{}, fn {k, v} -> {k, Enum.join(v, ", ")} end)
This turns:
[{"cookie", "a"}, {"cookie", "b"}] into %{"cookie", "a, b"}

Access the AST for generic functions in Julia

How can I access the abstract syntax tree for a generic function in Julia?
To recap: It looks like Simon was looking for the AST for a specific method associated with a generic function. We can get a LambdaStaticData object, which contains the AST, for a specific method as follows:
julia> f(x,y)=x+y
julia> f0 = methods(f, (Any, Any))[1]
((Any,Any),(),AST(:($(expr(:lambda, {x, y}, {{}, {{x, Any, 0}, {y, Any, 0}}, {}}, quote # none, line 1:
return +(x,y)
end)))),())
julia> f0[3]
AST(:($(expr(:lambda, {x, y}, {{}, {{x, Any, 0}, {y, Any, 0}}, {}}, quote # none, line 1:
return +(x,y)
end))))
julia> typeof(ans)
LambdaStaticData
Apparently this AST can either be an Expr object or a compressed AST object, represented as a sequence of bytes:
julia> typeof(f0[3].ast)
Array{Uint8,1}
The show() method for LambdaStaticData from base/show.jl illustrates how to decompress this, when encountered:
julia> ccall(:jl_uncompress_ast, Any, (Any, Any), f0[3], f0[3].ast)
:($(expr(:lambda, {x, y}, {{}, {{x, Any, 0}, {y, Any, 0}}, {}}, quote # none, line 1:
return +(x,y)
end)))
julia> typeof(ans)
Expr
Julia has four functions and four macros analog to those functions, used to inspect a lot about generic function's methods:
julia> f(x, y) = x + y
f (generic function with 1 method)
julia> methods(f)
# 1 method for generic function "f":
f(x,y) at none:1
Lowered code:
julia> code_lowered(f, (Int, Int))
1-element Array{Any,1}:
:($(Expr(:lambda, {:x,:y}, {{},{{:x,:Any,0},{:y,:Any,0}},{}}, :(begin # none, line 1:
return x + y
end))))
julia> #code_lowered f(1, 1) # Both `Int`s
...same output.
Typed code:
julia> code_typed(f, (Int, Int))
1-element Array{Any,1}:
:($(Expr(:lambda, {:x,:y}, {{},{{:x,Int64,0},{:y,Int64,0}},{}}, :(begin # none, line 1:
return (top(box))(Int64,(top(add_int))(x::Int64,y::Int64))::Int64
end::Int64))))
julia> #code_lowered f(1, 1) # Both `Int`s
...same output.
LLVM code:
julia> code_llvm(f, (Int, Int))
define i64 #julia_f_24771(i64, i64) {
top:
%2 = add i64 %1, %0, !dbg !1014
ret i64 %2, !dbg !1014
}
julia> #code_llvm f(1, 1) # Both `Int`s
...same output.
Native code:
julia> code_native(f, (Int, Int))
.text
Filename: none
Source line: 1
push RBP
mov RBP, RSP
Source line: 1
add RDI, RSI
mov RAX, RDI
pop RBP
ret
julia> #code_llvm f(1, 1) # Both `Int`s
...same output.
Type instability warnings (v0.4+):
julia> #code_warntype f(1, 1)
Variables:
x::Int64
y::Int64
Body:
begin # In[17], line 1:
return (top(box))(Int64,(top(add_int))(x::Int64,y::Int64))
end::Int64
Reflection and introspection
I'm not sure that there is an AST associated with a generic function because of multiple dispatch. If you're writing a function definition fbody, you should be able to get the AST by doing dump(quote(fbody)).

Resources