Replace symbols within an expression

I want to take an expression like

:(structOne.a[I] = tanh(structTwo.b[I]))

and replace all the composite values with other variables so I can make a function like

function f(I,a_new,b_new) # can't use composite values here
   a_new[I] = tanh(b_new[I]) # original expression with new symbols inserted
end
f(I,structOne.a,structTwo.b) # call

I know how to recursively find all the symbols in an expression, and I can look for the :(.) to isolate composite values, and I can use @gensym to make replacement symbol names.

But how can I make the expression a_new[I] = tanh(b_new[I]) which has the same form as the original expression but replaces the composites values with the new symbols?

Do you mean, something like this?

julia> ex = :(structOne.a[I] = tanh(structTwo.b[I]))
:(structOne.a[I] = tanh(structTwo.b[I]))

julia> rep(ex) = ex
       rep(ex::Expr) = ex.head == :. ? Symbol(ex.args[2].value, "_new") : ex
       rep!(ex) = ex
       rep!(ex::Expr) = (ex.args = map(rep, ex.args); foreach(rep!, ex.args); ex)
rep! (generic function with 2 methods)

julia> rep!(ex)
:(a_new[I] = tanh(b_new[I]))
4 Likes

Perfect example. Thanks!

This is my solution, inspired by @uniment’s solution but using “for loop”:

julia> makesym(expr::Expr) = Symbol(expr.args[2].value, "_new")
makesym (generic function with 1 method)

julia> function preprocess!(expr::Expr)
           for (i, arg) in enumerate(expr.args)
               if arg isa Expr
                   if arg.head === :(.)
                       expr.args[i] = makesym(arg)
                   else
                       preprocess!(arg)
                   end
               end
           end
           expr
       end
preprocess! (generic function with 1 method)

julia> expr = :(obj1.a[i] = tanh(obj2.b[i]))
:(obj1.a[i] = tanh(obj2.b[i]))

julia> preprocess!(expr)
:(a_new[i] = tanh(b_new[i]))
2 Likes