Also check out MacroTools.jl. It makes manipulating ASTs much cleaner.
On Friday, April 29, 2016 at 1:08:21 PM UTC-4, Josh Langsfeld wrote:
>
> Thank you both for the examples, those help a lot.
>
> On Friday, April 29, 2016 at 8:57:14 AM UTC-4, Stefan Karpinski wrote:
>>
>> String splicing + parsing completely destroys/ignores any pre-existing
>> hierarchical structure. If you don't manually get the
>> precedence/parentheses right, it will completely break. For example:
>>
>> julia> a, b = "x + y", "x - z";
>>
>> julia> parse("$a * $b")
>> :((x + y * x) - z)
>>
>> julia> a, b = :(x + y), :(x - z);
>>
>> julia> :($a * $b)
>> :((x + y) * (x - z))
>>
>>
>> The more complex the expressions you're working with, the worse this
>> gets. Similarly If there are problems with how expressions are printed,
>> you're hosed – you'll stringify some expression, splice it, parse it and
>> get total nonsense back. If you just splice expression objects, it will
>> just work and do the right thing, even for expressions which have no
>> possible string representation.
>>
>>
>> On Thu, Apr 28, 2016 at 10:21 PM, Yichao Yu <[email protected]> wrote:
>>
>>> On Thu, Apr 28, 2016 at 6:05 PM, Josh Langsfeld <[email protected]>
>>> wrote:
>>> > That's quite a strong statement to say never use parse. Could you
>>> explain? I
>>> > think manipulating strings is typically much easier than Expr objects.
>>>
>>> I don't believe that is true. String processing is slow, error prone
>>> and unsafe. Constructing a AST using splicing is more straightforward
>>> and more understandable than string splicing. Matching/processing an
>>> AST is also much easier than pattern matching and replacing in string.
>>> There's good reason AST processing are not done on strings directly.
>>>
>>> As an example, just compare the use of parse in the code provided.
>>> Note that strings are assumed to be replaced with symbols and
>>> expressions since that's what should be used for AST processing.
>>>
>>> parse("($(reduce((a,b)->"$a,$b",args)))->$f")
>>> vs
>>> :(($(args...))->$f)
>>>
>>> fun = reduce((a,b)->"$a + $b", dfun)
>>> parse("($(reduce((a,b)->"$a,$b",args)))->$fun")
>>> vs
>>> :(($(args...))->+($(dfun...)))
>>>
>>> >
>>> > On Thursday, April 28, 2016 at 8:38:27 AM UTC-4, Yichao Yu wrote:
>>> >>
>>> >>
>>> >> On Apr 28, 2016 7:15 AM, "Ben Lauwens" <[email protected]> wrote:
>>> >> >
>>> >> > Hi
>>> >> >
>>> >> > I like to create automatically functions that evaluates the total
>>> >> > derivative of a function with respect to time, eg.
>>> >> > given f(t, x1, x2) = t^2+x1*x2, I want to obtain
>>> >> > df/dt(t,x1,x2,dx1/dt,dx2/dt) = 2t+x1*dx2/dt+x2*dx1/dt,
>>> >> > d2f/dt2(t,x1,x2,dx1/dt,dx2/dt,d2x1/dt2,d2x2/dt) =
>>> >> > 2+2dx1/dt*dx2/dt+x1*d2x2/dt2+x2*d2x1/dt2, and so on.
>>> >> > The initial function is specified as a string as are the arguments.
>>> >> > The following code using the Calculus package works nicely:
>>> >> > using Calculus
>>> >> >
>>> >> > function total_derivatives_with_respect_to_time(f::AbstractString,
>>> >> > order::Int, names::AbstractString...)
>>> >> > derivs = Array(Function, order)
>>> >> > n = length(names)
>>> >> > args = AbstractString["t", names...]
>>> >> > derivs[1] = eval(parse("($(reduce((a,b)->"$a,$b",args)))->$f"))
>>> >> > if order > 1
>>> >> > fun = f
>>> >> > for o = 2:order
>>> >> > ∇fun = differentiate(fun, args)
>>> >> > dfun = AbstractString["$(∇fun[1])"]
>>> >> > for i = 1:n
>>> >> > push!(args, "d$(o-1)_$(names[i])")
>>> >> > end
>>> >> > for j = 2:(o-1)*n+1
>>> >> > push!(dfun, "($(∇fun[j])) * $(args[j+n])")
>>> >> > end
>>> >> > fun = reduce((a,b)->"$a + $b", dfun)
>>> >> > derivs[o] =
>>> eval(parse("($(reduce((a,b)->"$a,$b",args)))->$fun"))
>>> >> > end
>>> >> > end
>>> >> > return derivs
>>> >> > end
>>> >> >
>>> >> > derivs = total_derivatives_with_respect_to_time("t^2+x1*x2", 3,
>>> "x1",
>>> >> > "x2")
>>> >> > println(derivs[1](1.0, 2.0, 3.0))
>>> >> > println(derivs[2](1.0, 2.0, 3.0, 4.0, 5.0))
>>> >> > println(derivs[3](1.0, 2.0, 3.0, 4.0, 5.0, -1.0, -2.0))
>>> >>
>>> >> For calculating derivatives there may be better ways (forwarddiff and
>>> >> other packages). In general, generating functions with eval is fine
>>> as long
>>> >> as you dont do that in the performance critical part. However, you
>>> should
>>> >> never use parse and should construct the AST directly.
>>> >>
>>> >> > I have however the feeling that I am abusing the sequence of eval
>>> and
>>> >> > parse to automatically generate anonymous function. Is there a way
>>> to do
>>> >> > this more elegantly? Performance wise the code runs fine in Julia
>>> v0.5.
>>> >> >
>>> >> > Ben
>>>
>>
>>