Julia Metaprogramación: función para series matemáticas

Sep 11 2020

Estoy tratando de construir una función que genere una expresión para ser asignada a una nueva función en memoria. Podría estar malinterpretando la capacidad de la metaprogramación, pero estoy tratando de construir una función que genere una serie matemática y la asigne a una función como:

main.jl

function series(iter)
    S = ""
    for i in 1:iter
        a = "x^$i + "
        S = S*a
    end
    return chop(S, tail=3)
end

Entonces, esto construirá el patrón y estoy trabajando temporalmente con él en la respuesta:

julia> a = Meta.parse(series(4))
:(x ^ 1 + x ^ 2 + x ^ 3 + x ^ 4)

julia> f =eval(Meta.parse(series(4)))
120

julia> f(x) =eval(Meta.parse(series(4)))
ERROR: cannot define function f; it already has a value

Obviamente, eval no es lo que estoy buscando en este caso, pero ¿hay otra función que pueda usar? ¿O no es ésta una forma viable de realizar la tarea en Julia?

Respuestas

6 phipsgabler Sep 11 2020 at 18:03

El error real que obtiene no tiene nada que ver con la metaprogramación, sino con el hecho de que está reasignando f, al que se le asignó un valor antes:

julia> f = 10
10

julia> f(x) = x + 1
ERROR: cannot define function f; it already has a value
Stacktrace:
 [1] top-level scope at none:0
 [2] top-level scope at REPL[2]:1

Simplemente no le gusta eso. Llame a cualquiera de esas variables de manera diferente.

Ahora al problema conceptual. Primero, lo que haces aquí no es una metaprogramación "adecuada" en Julia: ¿por qué lidiar con cadenas y análisis? Puede trabajar directamente en expresiones:

julia> function series(N)
           S = Expr(:call, :+)
           for i in 1:N
               push!(S.args, :(x ^ $i))
           end
           return S
       end
series (generic function with 1 method)

julia> series(3)
:(x ^ 1 + x ^ 2 + x ^ 3)

Esto hace uso del hecho de que +pertenece a la clase de expresiones que se recopilan automáticamente en aplicaciones repetidas.

En segundo lugar, no llama evalal lugar apropiado. Supongo que quisiste decir "dame la función de x, siendo el cuerpo lo que series(4)regresa". Ahora, mientras lo siguiente funciona:

julia> f3(x) = eval(series(4))
f3 (generic function with 1 method)

julia> f3(2)
30

no es ideal, ya que compila el cuerpo cada vez que se llama a la función. Si hace algo así, es preferible expandir el código una vez en el cuerpo en la definición de la función:

julia> @eval f2(x) = $(series(4))
f2 (generic function with 1 method)

julia> f2(2)
30

Solo debes tener cuidado con la higiene aquí. Todo depende del hecho de que sepa que el cuerpo generado está formulado en términos de xy el argumento de la función coincide con eso. En mi opinión, la forma más juliana de implementar tu idea es a través de una macro:

julia> macro series(N::Int, x)
           S = Expr(:call, :+)
           for i in 1:N
               push!(S.args, :($x ^ $i))
           end
           return S
       end
@series (macro with 1 method)

julia> @macroexpand @series(4, 2)
:(2 ^ 1 + 2 ^ 2 + 2 ^ 3 + 2 ^ 4)

julia> @series(4, 2)
30

No quedan variables libres en la salida.

Finalmente, como se ha señalado en los comentarios, hay una función (y la macro correspondiente) evalpolyen Base que generaliza su caso de uso. Tenga en cuenta que esta función no usa la generación de código; usa una función generada bien diseñada, que en combinación con las optimizaciones da como resultado un código que generalmente es igual al código generado por macro.

3 PrzemyslawSzufel Sep 12 2020 at 00:25

Otra opción elegante sería utilizar el mecanismo de envío múltiple de Julia y enviar el código generado por tipo en lugar de valor.

@generated function series2(p::Val{N}, x) where N
    S = Expr(:call, :+)
    for i in 1:N
        push!(S.args, :(x ^ $i))
    end
    return S
end

Uso

julia> series2(Val(20), 150.5)
3.5778761722367333e43


julia> series2(Val{20}(), 150.5)
3.5778761722367333e43
nominalize Sep 29 2020 at 11:45

Esta tarea se puede lograr con comprensiones. Necesito RTFM ...

https://docs.julialang.org/en/v1/manual/arrays/#Generator-Expressions