Get a Symbol vector of all variable names in an Expr?

Is there a simple and type stable way to do this?

julia> f(:(x^2+sum(x,y-3,z)))

4-element Array{Symbol,1}:
 :x
 :x
 :y
 :z
1 Like

It’s pretty easy to walk expression trees, typing :(x^2+1) |> dump etc. will let you see how they are structured. Here’s a rough solution, but there are expressions which will trip this up:

julia> walk!(list) = ex -> begin
       ex isa Symbol && push!(list, ex)
       ex isa Expr && ex.head == :call && map(walk!(list), ex.args[2:end])
       list
       end
walk! (generic function with 1 method)

julia> :(x^2+sum(x,y-3,z)) |> walk!([])
4-element Array{Any,1}:
 :x
 :x
 :y
 :z
1 Like

MacroTools.jl Expression Walking can iterate expressions.

  1. How to get just x, y, z (from both ex1 and ex2) and not +,-,^,sum?
  2. How to return the list directly from postwalk instead of pushing to external list?
import MacroTools: postwalk

function get_ex_symbols1(ex)
    list = []
    postwalk(x -> x isa Symbol ? (push!(list, x); x) : x, ex)
    return Set{Symbol}(list)
end

function get_ex_symbols2(ex)
    list = []
    walk!(list) = ex -> begin
       ex isa Symbol && push!(list, ex)
       ex isa Expr && ex.head == :call && map(walk!(list), ex.args[2:end])
       list
    end
    Set{Symbol}(walk!([])(ex))
end

ex1 = :(x^2+sum(x,y-3,z))
ex2 = :(function f(x,y); x=x+1; y=y^2; z=sum(x,y) end);
julia> get_ex_symbols1(ex1)
Set{Symbol} with 7 elements:
  :+
  :^
  :y
  :-
  :z
  :sum
  :x

julia> get_ex_symbols1(ex2)
Set{Symbol} with 7 elements:
  :+
  :z
  :f
  :^
  :y
  :sum
  :x

julia> get_ex_symbols2(ex1)
Set{Symbol} with 3 elements:
  :y
  :z
  :x

julia> get_ex_symbols2(ex2)
Set{Symbol}()
1 Like