I have the following in my package:
"""
numparameters(f)
Returns the number of parameters of `f` for the method which has the most parameters.
"""
function numparameters(f)
if length(methods(f))>1
warn("Number of methods for f is greater than 1. Choosing linearity based off of method with most parameters")
end
numparm = maximum([length(m.sig.parameters) for m in methods(f)])
if VERSION < v"0.5-"
return numparm
else
return (numparm-1) #-1 in v0.5 since it add f as the first parameter.
end
end
This works for generic functions, and so it also works for anonymous functions on v0.5+. However, an anonymous function on v0.4 doesn't have methods, so this errors. I am wondering if anyone knows of a workaround.
On 0.4 this should work for anonymous functions:
length(Base.uncompressed_ast(f.code).args[1])
This extracts the list of formal arguments from the AST and gets its length. However this is pretty expensive so you shouldn't do it too often.
Related
I am trying to establish a group of anonymous functions to later call and receive a value from.
Here is a basic working example of the problem I'm facing.
expr = :(x+y)
f = eval((x,y,z)->expr)
This gives me the following response...
Then when I try to call it to be evaluated I just get the expression back.
f(1,2,3)
I would expect it to return 3 but instead it returns the original expression. How do I get it to evaluate the expression inside the anonymous function as it has been constructed here?
Thanks
I came across this spinet of code where the function rval_top_ingredients() was used to render a D3wordcloud before it was defined. I think that would throw an error in case of Python as the script is executed from top to bottom. Why did it work in R then? Thankyou.
output$wc_ingredients <- d3wordcloud::renderD3wordcloud({
ingredients_df <- rval_top_ingredients()
d3wordcloud(ingredients_df$ingredient, ingredients_df$nb_recipes, tooltip = TRUE)
})
rval_top_ingredients <- reactive({
recipes_enriched %>%
filter(cuisine == input$cuisine) %>%
arrange(desc(tf_idf)) %>%
head(input$nb_ingredients) %>%
mutate(ingredient = forcats::fct_reorder(ingredient, tf_idf))
})
R doesn’t differ from Python here: you can’t use a function before it’s defined. But, despite appearances to the contrary, this also isn’t happening here.
d3wordcloud::renderD3wordcloud is a special function call which doesn’t evaluate its arguments immediately. In fact, the argument is stored internally as an unevaluated expression and is only evaluated later after a certain trigger. By that time, rval_top_ingredients has been defined.
This is a pervasive pattern in Shiny, but you can harness this behaviour yourself. Consider the following:
f = function (expr) {}
f(g())
g = function () { stop('oh no!') }
This code works, since f never uses its argument, and since R uses lazy evaluation for function arguments: unlike most other languages, a function argument only gets evaluated once it is used. Arguments that are never used are never evaluated.
So, despite the fact that f(g()) appears to use g before it’s defined, the actual call to f never evaluates its arguments so there’s no issue. The only constraint is that the argument needs to be syntactically valid.
Here’s a slightly more meaningful example which does something useful (it creates a function that creates a log message before evaluating an expression:
make_verbose = function (expr) {
function () {
message(sprintf('Evaluating %s', deparse(substitute(expr))))
expr
}
}
verbose_g = make_verbose(g())
g = function () {
message('g was called!')
}
verbose_g()
Python doesn’t quite support this, since Python doesn’t have lazy and non-standard evaluation. But a similar situation still exists in Python:
def f():
g()
def g():
print('g()')
f()
Here, g() is seemingly used before it was defined; but this is only true if we’re reading the code textually from top top bottom without paying attention to scope. In reality, g() is only ever called after it was defined. The same is true in the R code you’ve posted.
Usually the multiple dispatch in julia is straightforward if one of the parameters in a function changes data type, for example Float64 vs Complex{Float64}. How can I implement multiple dispatch if the parameter is an integer, and I want two functions, one for even and other for odd values?
You may be able to solve this with a #generated function: https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1
But the simplest solution is to use an ordinary branch in your code:
function foo(x::MyType{N}) where {N}
if isodd(N)
return _oddfoo(x)
else
return _evenfoo(x)
end
end
This may seem as a defeat for the type system, but if N is known at compile-time, the compiler will actually select only the correct branch, and you will get static dispatch to the correct function, without loss of performance.
This is idiomatic, and as far as I know the recommended solution in most cases.
I expect that with type dispatch you ultimately still are calling after a check on odd versus even, so the most economical of code, without a run-time penatly, is going to be having the caller check the argument and call the proper function.
If you nevertheless have to be type based, for some reason unrelated to run-time efficiency, here is an example of such:
abstract type HasParity end
struct Odd <: HasParity
i::Int64
Odd(i::Integer) = new(isodd(i) ? i : error("not odd"))
end
struct Even <: HasParity
i::Int64
Even(i::Integer) = new(iseven(i) ? i : error("not even"))
end
parity(i) = return iseven(i) ? Even(i) : Odd(i)
foo(i::Odd) = println("$i is odd.")
foo(i::Even) = println("$i is even.")
for n in 1:4
k::HasParity = parity(n)
foo(k)
end
So here's other option which I think is cleaner and more multiple dispatch oriented (given by a coworker). Let's think N is the natural number to be checked and I want two functions that do different stuff depending if N is even or odd. Thus
boolN = rem(N,2) == 0
(...)
function f1(::Val{true}, ...)
(...)
end
function f1(::Val{false}, ...)
(...)
end
and to call the function just do
f1(Val(boolN))
As #logankilpatrick pointed out the dispatch system is type based. What you are dispatching on, though, is well established pattern known as a trait.
Essentially your code looks like
myfunc(num) = iseven(num) ? _even_func(num) : _odd_func(num)
I'm writing a function that I would find easier to write and read if it could define another function differently depending on input or runtime values of variables (and then use that function). The following illustrates the idea (even if defining a function inside a function is of no advantage in this simple example):
julia> function f(option::Bool)
if option
g() = println("option true")
g()
else
g() = println("option false")
g()
end
end;
WARNING: Method definition g() in module Main at REPL[1]:3 overwritten at REPL[1]:6.
julia> f(true)
option false
julia> f(false)
ERROR: UndefVarError: g not defined
in f(::Bool) at .\REPL[1]:7
Using the full function ... end syntax for g does not help either.
The question is: am I doing something wrong to get that warning and that unintended behavior, or Julia does not allow this for a reason? And if it can be done, how?
N.B. For my present need, I can just define two different functions, g1 and g2, and it seems to work; but what if there were many cases of g for just one task concept? I thought that a function, being a first-class object, could be manipulated freely: assigned to a variable, defined in a way or another depending on conditions, overwritten, etc.
P.S. I know I can compose a String and then parse-eval it, but that's an ugly solution.
You want to use anonymous functions. This is a known issue (this other issue also shows your problem).
function f(option::Bool)
if option
g = () -> println("option true")
else
g = () -> println("option false")
end
g
end
In v0.5 there's no performance difference between anonymous and generic functions, so there's no reason to not use anonymous functions. Note that there's also a sytnax for extended anonymous functions:
f = function (x)
x
end
and you can add dispatches via call overloading:
(T::typeof(f))(x,y) = x+y
so there's no reason to not use an anonymous function here.
Given a function object f, how do I find:
Function's name.
Module(s) of its method(s)?
In Julia 0.4 I was able to find name using f.env.name, but no tips for module. For Julia 0.5 I wasn't able to find any of two.
Name is easy: Symbol(f) or string(f) if you want a string
Module is, as you know going to be per method (i.e per type signature).
methods(f) with return a method table that prints out all the methods and where they are, in terms of files.
You can do [meth.module for meth in methods(f)] to get there modules
So to use an example, the collect function.
julia> using DataStructures #so we have some non-Base definitions
julia> Symbol(collect)
:collect
julia> methods(collect)
# 5 methods for generic function "collect":
collect(r::Range) at range.jl:813
collect{T}(::Type{T}, itr) at array.jl:211
collect(itr::Base.Generator) at array.jl:265
collect{T}(q::DataStructures.Deque{T}) at /home/ubuntu/.julia/v0.5/DataStructures/src/deque.jl:170
collect(itr) at array.jl:236
julia> [meth.module for meth in methods(collect)]
5-element Array{Module,1}:
Base
Base
Base
DataStructures
Base
julia> first(methods(collect, (Deque,))).module
DataStructures
#oxinabox's answer is correct. To add, typeof(f).name.mt.name is the v0.5 replacement for f.env.name. That can be useful to avoid the . that occurs when just applying string to a function introduced in a non-stdlib module. There also exists Base.function_name(f) which is probably less likely to break when the Julia version changes.
To get the module that a function (type) is introduced in, rather than the modules of individual methods, there's typeof(f).name.module, or the probably-better version, Base.function_module(f). The module of the method table is probably the same; that can be obtained through typeof(f).name.mt.module.
Note that f.env in v0.4 is a direct equivalent of typeof(f).name.mt, so on v0.4 the same f.env.name and f.env.module apply.
In Julia 1.x the commands are:
Base.nameof
Base.parentmodule
Since this two methods are exported, also nameof and parentmodule works.
Not exactly the answer but closely related: you can find the file name and line number of f using functionloc(f)