Why does Elixir have guard clauses for function definition in the module and not in function itself? I find the following to be more intuitive:
def fibonnaci(n) do
when n == 0, do: 0
when n == 1, do: 1
when n > 1 do
fibonnaci(n - 2) + fibonnaci(n - 1)
end
end
I am not sure if this is a question about how to write that code or about why the language was designed like that. Assuming it is the first, you have:
def fibonnaci(0), do: 0
def fibonnaci(1), do: 1
def fibonnaci(n), do: fibonnaci(n - 2) + fibonnaci(n - 1)
In case you want everything in the same function body, you could use a case:
def fibonnaci(n) do
case n do
0 -> ...
If you meant the latter, the answer may become clearer once you get more familiar with the language because the proposed example is not valid syntax (Elixir relies on a simple syntax with very few keywords where everything abides to the same rules).
Related
I'm very new to Julia but I've got a some background in Scheme/Rust/F#.
Today I wanted to make yesterday's AoC nicer without an explicit number of nested loops.
I arrived at this working solution, but I don't like the last if. In the languages mentioned above I would call a function (or use a computation expression) that gives me the first result that is not None. For Julia, I expected something to do that. It does, but unexpectedly in an eager fashion.
So When I tried return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended)), that also evaluated the second argument when the first already had a result—and thus crashed.
Is there a macro/lazy version or something that I didn't find? How are you supposed to handle a case like that?
I also thought about (short-circuited) or'ing them together, but I guess Julia's strictness in that matter spoils that.
using DataStructures
function find(r::Array{Int}, n, start = 1, which = nil())::Union{Int,Nothing}
if start <= length(r)
extended = cons(start, which)
with_current = sum(i -> r[i], extended)
if with_current == 2020 && n == 1
return prod(i -> r[i], extended)
else
# Unfortunately no :(
#return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended))
re = find(r, n, start + 1, which)
if isnothing(re)
return find(r, n - 1, start + 1, extended)
else
re
end
end
end
end
Let me comment more on it why it is not possible given the discussion in the comments.
In Julia function arguments are evaluated eagerly, so Julia evaluates both find(r, n, start + 1, which) and find(r, n - 1, start + 1, extended) before passing them to something function.
Now, with macros you have (I am not writing in a fully general case for simplicity and I hope I got the hygiene right :)):
julia> macro something(x, y)
quote
local vx = $(esc(x))
isnothing(vx) ? $(esc(y)) : vx
end
end
#something (macro with 1 method)
julia> #something 1 2
1
julia> #something nothing 2
2
julia> #something 1 sqrt(-1)
1
julia> #something nothing sqrt(-1)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
(in a full-blown version of the macro varargs and Some should be handled to replicate something exactly)
Piqued by Bogumił's answer I wanted to write my first Julia macro. It took some time and numerous attempts to figure out syntax, hygiene and escaping but I'm quite happy now.
I thought it might be worth sharing and provide opportunity for suggestions/improvements.
A lazy #something analog to Base.something
function _something_impl(thing)
:(something($(esc(thing))))
end
function _something_impl(thing, rest...)
quote
local evalued = $(esc(thing))
if isnothing(evalued)
$(_something_impl(rest...))
else
something(evalued)
end
end
end
macro something(things...)
_something_impl(things...)
end
Version without exceptions
As I found exceptions raised from a macro like this not quite suitable, I also made a version that falls back to nothing.
function _something_nothing_impl(thing)
quote
local evaluated = $(esc(thing))
if isa(evaluated, Some)
evaluated.value
else
evaluated
end
end
end
function _something_nothing_impl(thing, rest...)
quote
local evalued = $(esc(thing))
if isnothing(evalued)
$(_something_nothing_impl(rest...))
else
something(evalued)
end
end
end
macro something_nothing(things...)
_something_nothing_impl(things...)
end
Now I guess the recursive middle function could also generated by a macro. :)
How to count a number on in Elixir without built-in function such as Enum.count. Here is my code, Thanks so much
defmodule Ans do
#my_favorite_number 0
def sum([]) do
0
end
def sum([head|tail]) do
head + sum(tail)
end
def average([head|tail]) do
total = sum([head|tail])
iterations = Enum.count([head|tail])
output = total / iterations
end
end
You should read about tail-call optimization. The compiler makes use of this optimisation to prevent a new stack frame being created every recursive call, which will happen in your code. Here is an example of how to write the sum/1 function in a tail-recursive way. The main idea is to keep the return in an accumulator variable that is passed to each call, instead of building up the answer in the call stack:
def sum(list), do: sum(0, list)
def sum(acc, []), do: acc
def sum(acc, [head | tail]), do: sum(acc + head, tail)
For count, you can do something similar, but just add 1 instead of the value of the list item:
def count(list), do: count(0, list)
def count(acc, []), do: acc
def count(acc, [_head | tail]), do: count(acc + 1, tail)
While the answer by Adam is perfectly correct, to calculate the average you might do better (in one loop,) using more sophisticated accumulator.
defmodule M do
def avg(list), do: do_avg({0, 0}, list)
defp do_avg({cnt, sum}, []),
do: sum / cnt
defp do_avg({cnt, sum}, [h | t]),
do: do_avg({cnt + 1, sum + h}, t)
end
M.avg [1,2,3,4]
#⇒ 2.5
Here we do accumulate both count and total and calculate an average on the last step when the list is exhausted.
Also, you might return everything, as a tuple {cnt, sum, sum / cnt}, or as a map for better readability.
Like in R:
a <- 2
or even better
a ← 2
which should translate to
a = 2
and if possible respect method overloading.
= is overloaded (not in the multiple dispatch sense) a lot in Julia.
It binds a new variable. As in a = 3. You won't be able to use ← instead of = in this context, because you can't overload binding in Julia.
It gets lowered to setindex!. As in, a[i] = b gets lowered to setindex!(a, b, i). Unfortunately, setindex! takes 3 variables while ← can only take 2 variables. So you can't overload = with 3 variables.
But, you can use only 2 variables and overload a[:] = b, for example. So, you can define ←(a,b) = (a[:] = b) or ←(a,b) = setindex!(a,b,:).
a .= b gets lowered to (Base.broadcast!)(Base.identity, a, b). You can overload this by defining ←(a,b) = (a .= b) or ←(a,b) = (Base.broadcast!)(Base.identity, a, b).
So, there are two potentially nice ways of using ←. Good luck ;)
Btw, if you really want to use ← to do binding (like in 1.), the only way to do it is using macros. But then, you will have to write a macro in front of every single assignment, which doesn't look very good.
Also, if you want to explore how operators get lowered in Julia, do f(a,b) = (a .= b), for example, and then #code_lowered f(x,y).
No. = is not an operator in Julia, and cannot be assigned to another symbol.
Disclaimer: You are fully responsible if you will try my (still beginner's) experiments bellow! :P
MacroHelper is module ( big thanks to #Alexander_Morley and #DanGetz for help ) I plan to play with in future and we could probably try it here :
julia> module MacroHelper
# modified from the julia source ./test/parse.jl
function parseall(str)
pos = start(str)
exs = []
while !done(str, pos)
ex, pos = parse(str, pos) # returns next starting point as well as expr
ex.head == :toplevel ? append!(exs, ex.args) : push!(exs, ex)
end
if length(exs) == 0
throw(ParseError("end of input"))
elseif length(exs) == 1
return exs[1]
else
return Expr(:block, exs...) # convert the array of expressions
# back to a single expression
end
end
end;
With module above you could define simple test "language":
julia> module TstLang
export #tst_cmd
import MacroHelper
macro tst_cmd(a)
b = replace("$a", "←", "=") # just simply replacing ←
# in real life you would probably like
# to escape comments, strings etc
return MacroHelper.parseall(b)
end
end;
And by using it you could probably get what you want:
julia> using TstLang
julia> tst```
a ← 3
println(a)
a +← a + 3 # maybe not wanted? :P
```
3
9
What about performance?
julia> function test1()
a = 3
a += a + 3
end;
julia> function test2()
tst```
a ← 3
a +← a + 3
```
end;
julia> test1(); #time test1();
0.000002 seconds (4 allocations: 160 bytes)
julia> test2(); #time test2();
0.000002 seconds (4 allocations: 160 bytes)
If you like to see syntax highlight (for example in atom editor) then you need to use it differently:
function test3()
#tst_cmd begin
a ← 3
a ← a + a + 3 # parser don't allow you to use "+←" here!
end
end;
We could hope that future Julia IDEs could syntax highlight cmd macros too. :)
What could be problem with "solution" above? I am not so experienced julian so many things. :P (in this moment something about "macro hygiene" and "global scope" comes to mind...)
But what you want is IMHO good for some domain specific languages and not to redefine basic of language! It is because readability very counts and if everybody will redefine everything then it will end in Tower of Babel...
I'm trying to use subs in maple to replace derivatives in a longer formula with 0:
subs(diff(u(r),r) = 0, formula);
It seems that if formula only involves first derivatives of u(r) this works as I expect. For example,
formula := diff(u(r),r);
subs(diff(u(r),r) = 0, formula);
0
But if formula involves second derivatives I get a diff(0,r) in the result that won't go away even when using simplify:
formula := diff(u(r),r,r);
subs(diff(u(r),r) = 0, formula);
d
-- 0
dr
(My actual formula is quite long involving first and second derivatives of two variables. I know that all derivatives with respect to a certain variable are 0 and I'd like to remove them).
One way is to use the simplify command with so-called side-relations.
formula := diff(u(r),r,r) + 3*cos(diff(u(r),r,r))
+ diff(u(r),r) + x*(4 - diff(u(r),r,r,r)):
simplify( formula, { diff(u(r),r) = 0 } );
3 + 4 x
formula2 := diff(u(r,s),s,s) + 3*cos(diff(u(r,s),r,r))
+ diff(u(r,s),r) + x*(4 - diff(u(r,s),r,s,r,r)):
simplify( formula2, { diff(u(r,s),r) = 0 } );
/ 2 \
| d |
3 + |---- u(r, s)| + 4 x
| 2 |
\ ds /
[edit] I forgot to answer your additonal query about why you got d/dr 0 before. The answer is because you used subs instead of 2-argument eval. The former does purely syntactic substitution, and doesn't evaluate the result. The latter is the one that people often need, without knowing it, and does "evaluation at a (particular) point".
formulaA := diff(u(r),r,r):
subs(diff(u(r),r) = 0, formulaA);
d
--- 0
dr
%; # does an evaluation
0
eval(formulaA, diff(u(r),r) = 0);
0
formulaB := diff(u(r,s),s,r,r,s):
eval(formulaB, diff(u(r,s),r) = 0);
0
You can see that any evaluation of those d/dr 0 objects will produce 0. But it's is often better practice to use 2-argument eval than it is to do eval(subs(...)). People use subs because it sounds like "substitution", I guess, or they see others use it. Sometimes subs is the right tool for the job, so it's important to know the difference.
I am writing a simple example in Elixir and although it works I don't really understand how.
defmodule MyList do
def sum([],acc \\ 0), do: acc
def sum([head | tail], acc), do: sum(tail,acc + head)
end
When I call MyList.sum I get the expected result
sum([]) => 0
sum([1,2,3]) => 6
I cannot add a default param in the second sum because the compiler throws an error
def sum/2 has default values and multiple clauses, use a separate clause for declaring defaults
So my question is, how come sum([1,2,3]) works? It does not match any of the definitions.
Is the function still tail recursive?
When you have a multiclause with optional arguments, you can specify defaults as a body-less clause:
defmodule MyList do
def sum(list, acc \\ 0) # sets up default arguments
def sum([],acc), do: acc
def sum([head | tail], acc), do: sum(tail,acc + head)
end
Regarding your example, I'm just guessing, but I think that your code amounts to something like following:
defmodule MyList do
# implicitly generated due to default argument
def sum(list), do: sum(list, 0)
def sum([],acc), do: acc
def sum([head | tail], acc), do: sum(tail,acc + head)
end
Which is why sum([1,2,3]) works as well.
Edit:
The function is definitely tail recursive. If the last thing a function does is a call of another function (or itself), then it is a tail call. So in this case, when we call sum(tail, acc + head), first the arguments are calculated, and then a tail recursive call happens.