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)).