What’s a smart way to do 1+1 in elixir ? something like 1 |> Kernel.+(1)
?
“smart”
iex(1)> defmodule Plus do
...(1)> defmacro unquote(:+)(lhs, rhs), do: {:+, [], [lhs, rhs]}
...(1)> end
iex(2)> require Plus
Plus
iex(3)> Plus.+(1, 1)
2
I have a same question as well, I did one of exercism elixir tests. My code looks like this:
def pangram?(sentence) do
sentence
|> String.downcase()
|> String.replace(~r<[^a-z]>, "")
|> String.codepoints()
|> Enum.uniq()
|> length
|> Kernel.==(26)
end
Is this style acceptable?
or should I just do length == 26
in a separate line below the pipes?
In my opinion, it’s a function just like any other. So, I have no problem with it and occasionally do it in my code.
The smartest way I can think of:
iex> 2
2
This algorithm implementation has:
- constant runtime
- constant memory usage
- is easily understood, even by new programmers
- is likely to win against all other implementations in a code golf context.
Just dropping this in here, I invented it while learning elixir:
iex(1)> n = 1
1
iex(2)> %{^n => n} = %{n => n+1}
%{1 => 2}
iex(3)> n
2
You could pull in an external library like this
then do:
iex(1)> {:ok, l} = Luex.init()
iex(2)> {:ok, {n} = Luex.dostring(l, "return 1+1")
iex(3)> n
2.0
Javascript has a great math, so why can’t you use your browser to calculate?
iex(5)> Drab.Core.exec_js!(socket, "1+1")
2
Even without browser. Why let elixir do all that dumb work? Hand the stupid work over to node!
This probably doesn’t fall into the category of “smart”.
def add(n, m) do
ns = for _ <- 1..n, do: 1
ms = for _ <- 1..m, do: 1
Enum.count(ns ++ ms)
end
Bah, infix math is stupid, the True Smart way is postfix (and can’t forget error handling!)!
╰─➤ iex
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> postfix_interpreter = &case Enum.reduce(String.split(&1), [], fn
...(1)> "+", [top, next | stack] -> [top + next | stack]
...(1)> # Add more ops here as wanted
...(1)> input, stack -> case Integer.parse(input) do {i, ""} -> [i | stack]; _ -> throw "Invalid Operator for the current stack" end
...(1)> end) do [value] -> value; _ -> throw "Too many values left on stack" end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(2)> postfix_interpreter.("1 1 +")
2
/s
If you defined the macro as
defmacro unquote(:+)(lhs, rhs), do: Kernel.+(lhs, rhs)
It would do the addition at compile time, so effectively producing @Qqwy’s answer.
The erlang compiler is smart enough to constant-fold a 1 + 1
expression to 2
at compile time even without macros
Why not do go down to the logic gates? A we all know, bit summary is: sum = xor(left, right), carry = and(left, right), so:
iex(15)> use Bitwise
Bitwise
iex(16)> left = 1; right = 1
1
iex(17)> << band(left, right) :: size(1), bxor(left, right) :: size(1) >>
<<2::size(2)>>
you really shouldn’t be using BEAM languages for doing math. Use a port.
1 |> fn x -> System.cmd("expr", [(x |> Integer.to_string), "+", "1"]) end.() |> fn {str, 0} -> str end.() |> String.trim |> String.to_integer
By far my favourite topic on this whole forum.
But this is how you’d do it in other languages as well. I wanted something Elixir specific
That doesn’t make it a worse Elixir answer .
I think i’ve finally come up with best way of doing 1+1. (it actually adds any number)
defmodule Add do
def add(0, 0), do: 0
def add(l, r) do
apply(__MODULE__, :"#{l}", [r])
rescue
_ in FunctionClauseError -> apply(__MODULE__, :"$handle_undefined_function", [l, [r]])
end
def unquote(:"$handle_undefined_function")(l, [r]) when is_atom(l) when is_integer(r) do
# :code.purge(__MODULE__)
l = String.to_integer(to_string(l))
file = File.read!(__ENV__.file)
{:ok, ast} = Code.string_to_quoted(file)
{:defmodule, meta1, [
{:__aliases__, meta1, [:Add]}, [do:
{:__block__, meta3, [
{:def, meta4, [{:add, meta5, [0, 0]}, [do: 0]]} | rest]}
]
]} = ast
new_defs = [
{:def, meta4, [{:add, [], [l, r]}, [do: l + r]]} | Process.get(:old_defs, [])
]
Process.put(:old_defs, new_defs)
new_ast = {:defmodule, meta1, [
{:__aliases__, meta1, [:Add]}, [do:
{:__block__, meta3,
[
[new_defs] |
[{:def, meta4, [{:add, meta5, [0, 0]}, [do: 0]]} | rest]
]
}]
]}
{{:module, Add, _, _}, _} = Code.eval_quoted(new_ast, [], [file: __ENV__.file])
apply(__MODULE__, :add, [l, r])
end
end
here’s how you use it:
iex()> Add.add(1, 2)
3
iex()> Add.add(4, 4)
8
And here’s the best part! it will only ever add two numbers together once. No more will you have to worry about your programs forgetting that 1+1 = 2. Every time it adds two numbers together, it stores the result in the current process, defines a new function head, recompiles the module, and returns the result.
Update
Here are some benchmarks:
iex()> {time1, _} = :timer.tc(fn() -> for i<-0..100, do: Add.add(i,i); :ok end)
{2520664, :ok}
iex()> {time2, _} = :timer.tc(fn() -> for i<-0..100, do: Add.add(i,i); :ok end)
{756, :ok}
as you can see the first time, it takes 2520664
microseconds. The second time however only takes 756!
But hasn’t this the culprit of beeing only possible if you have the sources available?
file = File.read!(__ENV__.file)