While experimenting with Julia 1.0, I've noticed that I can do something like this:
x\y = 1
The REPL then shows:
\ (generic function with 1 method)
which means its a valid assignment (the interpreter doesn't complain). However, x, y, and x\y all remain undefined.
What is the meaning of such expression?
It is a new function definition that (kind of) shadows the left division operator \ in Base, since the left division operator is already defined for some types in Julia. The new function definition is \(x,y) = 1 (the names of function parameters do not matter) which works for all types of variables. This will prevent julia from loading Base.\ due to name conflict. No matter what the input is your new \ will return the same value.
julia> x\y = 5
julia> a = 3; b = 4;
julia> a\b
5
julia> c = "Lorem ipsum"; d = "dolor";
julia> c\d
5
If you have already used the \ that is defined in Base, your redefinition will throw an error saying that extending Base.\ requires an explicit import with import Base.\. The behavior of defining \ after import Base.\ however will be different. It will extend the operator Base.\.
julia> 1\[1,3]
2-element Array{Float64,1}:
1.0
3.0
julia> import Base.\
julia> x\y=3
\ (generic function with 152 methods)
julia> 1\[1,3]
2-element Array{Int64,1}:
3
3
Related
I'm trying to build a function that will output an expression to be assigned to a new in-memory function. I might be misinterpreting the capability of metaprogramming but, I'm trying to build a function that generates a math series and assigns it to a function such as:
main.jl
function series(iter)
S = ""
for i in 1:iter
a = "x^$i + "
S = S*a
end
return chop(S, tail=3)
end
So, this will build the pattern and I'm temporarily working with it in the repl:
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
Obviously eval isn't what I'm looking for in this case but, is there another function I can use? Or, is this just not a viable way to accomplish the task in Julia?
The actual error you get has to do nothing with metaprogramming, but with the fact that you are reassigning f, which was assigned a value before:
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
It just doesn't like that. Call either of those variables differently.
Now to the conceptual problem. First, what you do here is not "proper" metaprogramming in Julia: why deal with strings and parsing at all? You can work directly on expressions:
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)
This makes use of the fact that + belongs to the class of expressions that are automatically collected in repeated applications.
Second, you don't call eval at the appropriate place. I assume you meant to say "give me the function of x, with the body being what series(4) returns". Now, while the following works:
julia> f3(x) = eval(series(4))
f3 (generic function with 1 method)
julia> f3(2)
30
it is not ideal, as you newly compile the body every time the function is called. If you do something like that, it is preferred to expand the code once into the body at function definition:
julia> #eval f2(x) = $(series(4))
f2 (generic function with 1 method)
julia> f2(2)
30
You just need to be careful with hygiene here. All depends on the fact that you know that the generated body is formulated in terms of x, and the function argument matches that. In my opinion, the most Julian way of implementing your idea is through a 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 free variables remaining in the output.
Finally, as has been noted in the comments, there's a function (and corresponding macro) evalpoly in Base which generalizes your use case. Note that this function does not use code generation -- it uses a well-designed generated function, which in combination with the optimizations results in code that is usually equal to the macro-generated code.
Another elegant option would be to use the multiple-dispatch mechanism of Julia and dispatch the generated code on type rather than value.
#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
Usage
julia> series2(Val(20), 150.5)
3.5778761722367333e43
julia> series2(Val{20}(), 150.5)
3.5778761722367333e43
This task can be accomplished with comprehensions. I need to RTFM...
https://docs.julialang.org/en/v1/manual/arrays/#Generator-Expressions
For example in the code below, x is defining the domain, but why is there the double dot between 0 and 4pi?
using ApproxFun
x=Fun(identity,0..4π)
.. is an operator (like e.g. +) but it does not have a default definition. You can define it to to whatever you want:
julia> ..(a, b) = println(a, ", ", b)
.. (generic function with 1 method)
julia> "hello" .. "world"
hello, world
The Julia package IntervalArithmetic uses it to construct an interval, e.g.
julia> using IntervalArithmetic
julia> 4..5
[4, 5]
julia> typeof(4..5)
Interval{Float64}
and I suspect this is what it is used for in your code example.
.. is not part of Julia, rather part of the packages used by ApproxFun.
It is used to represent intervals, see the code below
julia> u = 1..3
1..3
julia> dump(u)
Interval{:closed,:closed,Int64}
left: Int64 1
right: Int64 3
So this is just a convenience constructor for the Interval object, see:
julia> 1..3 === Interval{:closed,:closed,Int64}(1,3)
true
I am coming from Fortran and I use global vectors of data over the full program. Usually I declare a module:
module xyz
real, allocatable :: vector(:,:,:) ! a 3 dim vector, undefined
end module
Now, in some place, let say Subroutine (Function) A, I am allocating memory for it and initialize to some values:
allocate(vector(10,20,30))
vector = ran()
Now, in any other unit of the program (Subroutine or function B, C, D...), if I am using the module, i.e:
using xyz
The above declared vector is available.
I was not able to obtain this behavior in the new technological wonder Julia 1.1. The scope rules are just giving a headache.
In Julia the rules for accessing variables from other modules are explained in detail here.
The key issues in your situation are the following things:
a variable is visible after using only if it is exported in the module
you are allowed to access the variable in other modules
you are not allowed to rebind a variable from other modules
This means that global variable binding creation operation is private to the module.
Here is a simple example module definition:
module M
export x
x = Int[]
function rebindx()
global x = Int[]
end
end
now assume you define and later use it in REPL (it could be any other module)
julia> module M
export x
x = Int[]
function rebindx()
global x = Int[]
end
end
Main.M
julia> using .M
Now you can access x:
julia> x
0-element Array{Int64,1}
julia> push!(x, 1)
1-element Array{Int64,1}:
1
julia> x
1-element Array{Int64,1}:
1
julia> x[1] = 10
10
julia> x
1-element Array{Int64,1}:
10
But not rebind x:
julia> x = 0
ERROR: cannot assign variable M.x from module Main
However, you can call a function defined inside M module to change the binding of x like this:
julia> x
1-element Array{Int64,1}:
10
julia> M.rebindx()
0-element Array{Int64,1}
julia> x
0-element Array{Int64,1}
This was possible because rebindx was defined inside module M so it has the right to change the binding of variable x defined in this module.
Say I enter:
julia> X = randn(3,4)
3x4 Array{Float64,2}:
-0.862092 0.78568 0.140078 -0.0409951
-0.157692 0.0838577 1.38264 -0.296809
1.40242 -0.628556 -0.500275 0.258898
What functions were called to produce the output given?
Note that overloading Base.show does not seem to be sufficient to change this behaviour, so I'm unsure where to go.
julia> Base.show(io::IO, A::Array{Float64, 2}) = println("Humbug")
show (generic function with 120 methods)
julia> X
3x4 Array{Float64,2}:
-0.862092 0.78568 0.140078 -0.0409951
-0.157692 0.0838577 1.38264 -0.296809
1.40242 -0.628556 -0.500275 0.258898
Is it perhaps the case that I would have to change Base/array.jl source code and rebuild julia before such a change would work? Note the difference between this and a user defined type:
julia> type foo
x::Float32
s::ASCIIString
end
julia> ob = foo(1., "boo")
foo(1.0f0,"boo")
julia> Base.show(io::IO, someob::foo) = print("Humbug!")
show (generic function with 123 methods)
julia> ob
Humbug!
well, you should overload display():
julia> Base.display(A::Array{Float64, 2}) = println("Humbug")
display (generic function with 11 methods)
julia> X
Humbug
you can find the definition in REPL.jl.
I have a function, f. I want to add a method that takes any container of Strings. For example, I want to write a method that generates the following when needed:
f(xs::Array{String, 1}) = ...
f(xs::DataArray{String, 1}) = ...
f(xs::ITERABLE{String}) = ...
Is this possible to do in Julia's type system? Right now, I'm using a macro to write a specialized method when I need it.
#make_f(Array{String, 1})
#make_f(DataArray{String, 1})
This keeps things DRY, but it feels...wrong.
Can't you just use duck typing? I.e., just assume that you're feeding the function an object of the right type and throw an error if at some point e.g. you don't have a string in your iterable.
This should improve once you can really talk about iterables using traits; currently there is no iterable type. Scott's answer, for example, will not work with a tuple of strings, even though that is iterable.
E.g.
julia> f(x) = string(x...) # just concatenate the strings
f (generic function with 1 method)
julia> f(("a", "á"))
"aá"
julia> f(["a", "á"])
"aá"
julia> f(["a" "b"; "c" "d"]) # a matrix of strings!
"acbd"
At least in Julia 0.4, the following should work:
julia> abstract Iterable{T} <: AbstractVector{T}
julia> f{T<:Union{Vector{String},Iterable{String}}}(xs::T) = 1
f (generic function with 1 method)
julia> x = String["a", "é"]
2-element Array{AbstractString,1}:
"a"
"é"
julia> f(x)
1