Is there a way to get the surface syntax AST of a method?
according to the docs http://docs.julialang.org/en/stable/devdocs/reflection/ there is a function/macro for everything below surface AST, starting with code_lowered for lowered AST.
it would be great to have something like
f(a,b) = 2*a + b
#code_surface f(1,2)
# :(2a + b)
where code_surface shall return f's definition in form of a standard Expr abstract syntax tree.
#code_expr from CodeTracking.jl returns the Expr of the method definition (needs the Revise.jl package to also be installed).
julia> f(a,b) = 2*a + b
f (generic function with 1 method)
julia> using CodeTracking
julia> #code_expr f(1, 2)
:(f(a, b) = begin
#= REPL[65]:1 =#
2a + b
end)
julia> #code_string f(1, 2)
"f(a,b) = 2*a + b"
(There was discussion about adding a similar feature to base Julia, but it seems unlikely to happen at this point.)
Having the LHS of the method definition in the result helps with knowing which method was applied to the given arguments
julia> f(a::String,b::String) = a * b
f (generic function with 2 methods)
julia> #code_expr f("hi", " friend")
:(f(a::String, b::String) = begin
#= REPL[74]:1 =#
a * b
end)
julia> #code_expr f(2, 5)
:(f(a, b) = begin
#= REPL[65]:1 =#
2a + b
end)
But if you only need the Expr of the function body, you can extract that part of the expression from the result (ex = #code_expr(f(2, 5)); ex.args[2]).
Related
I came across Julia in some graduate research and have done a few projects already in C++. I'm trying to "translate" some of my C++ work into Julia to compare performance, among other things.
Basically what I'm trying to do is implement something like the functional library from C++ such that I can do something like
g(x, b) = x*b # Modifier function
A = [1,2,...] # Some array of values
f(x) = 1 # Initialize the function
### This is the part that I am seeking
for i = 1:length(A)
f(x) = f(x)*g(x, A[i]) # This thing right here
end
###
and then be able to call f(x) and get the value of all the g(x, _) terms included (similar to using bind in C++)
I'm not sure if there is native syntax to support this, or if I'll need to look into some symbolic representation stuff. Any help is appreciated!
You are probably looking for this:
julia> g(x, b) = x * b
g (generic function with 1 method)
julia> bind(g, b) = x -> g(x, b) # a general way to bind the second argument in a two argument function
bind (generic function with 1 method)
julia> A = [1, 2, 3]
3-element Vector{Int64}:
1
2
3
julia> const f = bind(g, 10) # bind a second argument of our specific function g to 10; I use use const for performance reasons only
#1 (generic function with 1 method)
julia> f.(A) # broadcasting f, as you want to apply it to a collection of arguments
3-element Vector{Int64}:
10
20
30
julia> f(A) # in this particular case this also works as A*10 is a valid operation, but in general broadcasting would be required
3-element Vector{Int64}:
10
20
30
In particular for fixing the first and the second argument of the two argument function there are standard functions in Julia Base that are called Base.Fix1 and Base.Fix2 respectively.
You may be looking for the function composition operator ∘
help?> ∘
"∘" can be typed by \circ<tab>
search: ∘
f ∘ g
Compose functions: i.e. (f ∘ g)(args...) means f(g(args...)). The ∘ symbol can be
entered in the Julia REPL (and most editors, appropriately configured) by typing
\circ<tab>.
Function composition also works in prefix form: ∘(f, g) is the same as f ∘ g. The
prefix form supports composition of multiple functions: ∘(f, g, h) = f ∘ g ∘ h and
splatting ∘(fs...) for composing an iterable collection of functions.
Examples
≡≡≡≡≡≡≡≡≡≡
julia> map(uppercase∘first, ["apple", "banana", "carrot"])
3-element Vector{Char}:
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase)
julia> fs = [
x -> 2x
x -> x/2
x -> x-1
x -> x+1
];
julia> ∘(fs...)(3)
3.0
so, for example
julia> g(b) = function(x); x*b; end # modifier function
g (generic function with 1 method)
julia> g(3)(2)
6
julia> f() = 3.14
f (generic function with 1 method)
julia> h = g(2) ∘ f
var"#1#2"{Int64}(2) ∘ f
julia> h()
6.28
For a fixed n, I would like to create a function with n variables
f(x_1, ..., x_n)
For example if n=3, I would like to create an algorithm such that
f(x_1, x_2, x_3) = x_1 + x_2 + x_3
It would be very nice to have an algorithm for every n:
f(x_1, ..., x_n) = x_1 + ... + x_n
I don't know how to declare the function and how can create the n variables.
Thank you for your help,
In Julia you can just do
function f(x...)
sum(x)
end
And now:
julia> f(1,2,3)
6
Note that within the function f, x is just seen as a Tuple so you can do whatever you want (including asking for type of elements etc).
More generally you can define function f(x...;y...). Let us give it a spin
function f(x...;y...)
#show x
#show Dict(y)
end
And now run it:
julia> f(1,2,"hello";a=22, b=777)
x = (1, 2, "hello")
Dict(y) = Dict(:a => 22, :b => 777)
Dict{Symbol, Int64} with 2 entries:
:a => 22
:b => 777
Finally, another one (perhaps less elegant) way could be:
g(v::NTuple{3,Int}) = sum(v)
This forces v to be a 3-element Tuple and g be called as g((1,2,3))
If your n is small, you can do this manually thanks to multiple dispatch.
julia> f(x) = x + 1 # Method definition for one variable.
f (generic function with 1 method)
julia> f(x, y) = x + y + 1 # Method definition for two variables.
f (generic function with 2 methods)
julia> f(2)
3
julia> f(2, 4)
7
You could use macro programming to generate a set of these methods automatically, but that quickly becomes complicated. You are likely better off structuring your function so that it operates on either a Vector or a Tuple of arbitrary length. The definition of the function will depend on what you want to do. Some examples which expect x to be a Tuple, Vector, or other similar datatype are below.
julia> g(x) = sum(x) # Add all the elements
g (generic function with 1 method)
julia> h(x) = x[end - 1] # Return the second to last element
h (generic function with 1 method)
julia> g([10, 11, 12])
33
julia> h([10, 11, 12])
11
If you would rather the function accept an arbitrary number of inputs rather than a single Tuple or Vector as you wrote in the original question, then you should define a method for the functions with the slurping operator ... as below. Note that the bodies of the function definitions and the outputs from the functions are exactly the same as before. Thus, the slurping operator ... is just for syntactical convenience. The functions below still operate on the Tuple x. The function arguments are just slurped into the tuple before doing anything else. Also note that you can define both the above and below methods simultaneously so that the user can choose either input method.
julia> g(x...) = sum(x) # Add all the variables
g (generic function with 2 methods)
julia> h(x...) = x[end - 1] # Return the second to last variable
h (generic function with 2 methods)
julia> g(10, 11, 12)
33
julia> h(10, 11, 12)
11
Finally, another trick that is sometimes useful is to call a function recursively. In other words, to have a function call itself (potentially using a different method).
julia> q(x) = 2 * x # Double the input
q (generic function with 1 method)
julia> q(x...) = q(sum(x)) # Add all the inputs and call the function again on the result
q (generic function with 2 methods)
julia> q(3)
6
julia> q(3, 4, 5)
24
I'm trying to make a function that compose a function f(x) by itself N times, something like that:
function CompositionN(f,N)
for i in 1:N
f(x) = f(f(x))
end
return f(x)
I need that the function CompositionN returns another function, not a value.
The solution with ntuple and splatting works very well up to a certain number of compositions, say 10, and then falls off a performance cliff.
An alternative solution, using reduce, is fast for large numbers of compositions, n, but comparatively slow for small numbers:
compose_(f, n) = reduce(∘, ntuple(_ -> f, n))
I think that the following solution is optimal for both large and small n:
function compose(f, n)
function (x) # <- this is syntax for an anonymous function
val = f(x)
for _ in 2:n
val = f(val)
end
return val
end
end
BTW: it is the construction of the composed function which is faster in the approach suggested here. The runtimes of the resulting functions seem to be identical.
You can take advantage of the ∘ function, which allows you to compose multiple functions:
julia> composition(f, n) = ∘(ntuple(_ -> f, n)...)
composition (generic function with 1 method)
julia> composition(sin, 3)(3.14)
0.001592651569876818
julia> sin(sin(sin(3.14)))
0.001592651569876818
Here's a recursive approach:
julia> compose(f, n) = n <= 1 ? f : f ∘ compose(f, n-1)
compose (generic function with 1 method)
julia> compose(x -> 2x, 3)(1)
8
If we're willing to do a little type piracy, we can use the power operator ^ on functions to represent n-th order self-composition:
julia> Base.:^(f::Union{Type,Function}, n::Integer) = n <= 1 ? f : f ∘ f^(n-1)
julia> f(x) = 2x
f (generic function with 1 method)
julia> (f^3)(1)
8
Is there any way to run name resolution on an arbitrary expression without running it? e.g. I would like to take an expression such as
quote
x = 1
y = 2*x + 1
z = x^2 - 1
f(x) = 2*x + 1
end
and be told that the names defined in the scope of this block are x, y, z, f and the names *, +, ^, - are pulled in from outside the scope of this block. Bonus points if it can tell me that there's a sub-scope defined in the body of f which creates it's own name x and pulls in + from an enclosing scope.
This question appeared in the Julia Zulip community
Thanks to Takafumi for showing me how to solve this on Zulip
We can get a list of locally defined names in the outermost scope of a julia expression like so:
ex = quote
x = 1
y = 2*x + 1
z = x^2 - 1
f(x) = 2*x + 1
end
using JuliaVariables, MLStyle
function get_locals(ex::Expr)
vars = (solve_from_local ∘ simplify_ex)(ex).args[1].bounds
map(x -> x.name, vars)
end
julia> get_locals(ex)
4-element Array{Symbol,1}:
:f
:y
:z
:x
and we can get the symbols pulled in from outside the scope like this:
_get_outers(_) = Symbol[]
_get_outers(x::Var) = x.is_global ? [x.name] : Symbol[]
function _get_outers(ex::Expr)
#match ex begin
Expr(:(=), _, rhs) => _get_outers(rhs)
Expr(:tuple, _..., Expr(:(=), _, rhs)) => _get_outers(rhs)
Expr(_, args...) => mapreduce(_get_outers, vcat, args)
end
end
get_outers(ex) = (unique! ∘ _get_outers ∘ solve_from_local ∘ simplify_ex)(ex)
julia> get_outers(ex)
6-element Array{Symbol,1}:
:+
:*
:-
:^
How can I define an outer constructor that has same number of arguments as the field values? What I want to do is something like this:
struct data
x
y
end
function data(x, y)
return data(x-y, x*y)
end
But it obviously causes stackoverflow.
Based on the various helpful comments, thanks to all, I changed my answer. Here is an example in Julia 1.0.0 of what you may be after. I am learning Julia myself, so maybe further comments can improve this example code.
# File test_code2.jl
struct Data
x
y
Data(x, y) = new(x - y, x * y)
end
test_data = Data(105, 5)
println("Constructor example: test_data = Data(105, 5)")
println("test_data now is...: ", test_data)
#= Output
julia> include("test_code2.jl")
Constructor example: test_data = Data(105, 5)
test_data now is...: Data(100, 525)
=#
This works for me
julia> struct datatype
x
y
end
julia> function datatype_create(a,b)
datatype(a - b, a * b)
end
datatype_create (generic function with 1 method)
julia> methods(datatype_create)
# 1 method for generic function "datatype_create":
[1] datatype_create(a, b) in Main at none:2
julia> methods(datatype)
# 1 method for generic function "(::Type)":
[1] datatype(x, y) in Main at none:2
julia> a = datatype_create(105,5)
datatype(100, 525)
julia> b = datatype_create(1+2im,3-4im)
datatype(-2 + 6im, 11 + 2im)
julia> c = datatype_create([1 2;3 4],[4 5;6 7])
datatype([-3 -3; -3 -3], [16 19; 36 43])
julia> d = datatype_create(1.5,0.2)
datatype(1.3, 0.30000000000000004)
If you are absolutely Ideologically Hell Bent on using an outer constructor, then you can do something like this
julia> datatype(a,b,dummy) = datatype(a - b,a * b)
datatype
julia> e = datatype(105,5,"dummy")
datatype(100, 525)
Antman's solution using the power of MACRO
julia> macro datatype(a,b)
return :( datatype($a - $b , $a * $b) )
end
#datatype (macro with 1 method)
julia> f = #datatype( 105 , 5 )
datatype(100, 525)