Julia Metaprogramming: Matematiksel Seriler İçin İşlev
Yeni bir bellek içi işleve atanacak bir ifade çıkaracak bir işlev oluşturmaya çalışıyorum. Metaprogramlama yeteneğini yanlış yorumluyor olabilirim, ancak bir matematik serisi oluşturan ve aşağıdaki gibi bir işleve atayan bir işlev oluşturmaya çalışıyorum:
main.jl
function series(iter)
S = ""
for i in 1:iter
a = "x^$i + "
S = S*a
end
return chop(S, tail=3)
end
Bu, kalıbı oluşturacak ve ben de geçici olarak onunla çalışıyorum:
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
Açıkçası eval bu durumda aradığım şey değil ama kullanabileceğim başka bir işlev var mı? Ya da bu Julia'daki görevi başarmanın uygun bir yolu değil mi?
Yanıtlar
Aldığınız gerçek hata, meta programlamayla hiçbir şey yapmamalı, ancak fdaha önce bir değer atanmış olan yeniden atadığınız gerçeğiyle :
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
Bundan hoşlanmıyor. Bu değişkenlerden herhangi birini farklı şekilde çağırın.
Şimdi kavramsal soruna. Birincisi, burada yaptığınız şey Julia'da "doğru" metaprogramlama değildir: neden dizelerle uğraşıp hiç ayrıştırmayasınız? Doğrudan ifadeler üzerinde çalışabilirsiniz:
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)
Bu, +tekrarlanan uygulamalarda otomatik olarak toplanan ifade sınıfına ait olgudan yararlanır.
İkincisi, evaluygun yerde aramıyorsun . Sanırım x, series(4)"geri dönen bedenle birlikte bana işlevini ver " demek istedin. Şimdi, aşağıdakiler çalışırken:
julia> f3(x) = eval(series(4))
f3 (generic function with 1 method)
julia> f3(2)
30
işlev her çağrıldığında vücudu yeni derlediğiniz için ideal değildir. Böyle bir şey yaparsanız, işlev tanımında kodu vücuda bir kez genişletmek tercih edilir:
julia> @eval f2(x) = $(series(4))
f2 (generic function with 1 method)
julia> f2(2)
30
Burada sadece hijyene dikkat etmeniz gerekiyor. Her şey, üretilen gövdenin açısından formüle edildiğini xve işlev argümanının bununla eşleştiğini bilmenize bağlıdır . Bence fikrinizi uygulamanın en Julian yolu bir makro yoluyla:
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
Çıktıda boş değişken kalmadı.
Son olarak, yorumlarda belirtildiği gibi, evalpolyBase'de kullanım durumunuzu genelleyen bir işlev (ve karşılık gelen makro) vardır. Bu fonksiyon anlamına Not değil bunun kodu optimizasyonlar ile kombinasyon halinde genellikle makro oluşturulan koda eşit iyi tasarlanmış oluşturulan işlevi kullanır - kod üretimini kullanır.
Diğer bir zarif seçenek Julia'nın çoklu gönderme mekanizmasını kullanmak ve üretilen kodu değer yerine türe göre göndermek olabilir.
@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
Kullanım
julia> series2(Val(20), 150.5)
3.5778761722367333e43
julia> series2(Val{20}(), 150.5)
3.5778761722367333e43
Bu görev kavrayışla gerçekleştirilebilir. RTFM'ye ihtiyacım var ...
https://docs.julialang.org/en/v1/manual/arrays/#Generator-Expressions