Smart way to do 1+1?

What’s a smart way to do 1+1 in elixir ? something like 1 |> Kernel.+(1) ?

5 Likes

“smart” :smiley:

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
16 Likes

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?

3 Likes

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.

6 Likes

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.

:angel:

27 Likes

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
6 Likes

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
3 Likes

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
8 Likes

Even without browser. Why let elixir do all that dumb work? Hand the stupid work over to node! :slight_smile:

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
1 Like

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

14 Likes

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.

2 Likes

The erlang compiler is smart enough to constant-fold a 1 + 1 expression to 2 at compile time even without macros :slight_smile:

14 Likes

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)>>
9 Likes

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
7 Likes

By far my favourite topic on this whole forum.

7 Likes

But this is how you’d do it in other languages as well. I wanted something Elixir specific :slight_smile:

That doesn’t make it a worse Elixir answer :stuck_out_tongue_winking_eye:.

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!

13 Likes

But hasn’t this the culprit of beeing only possible if you have the sources available?

   file = File.read!(__ENV__.file)