tio407

tio407

Bitwise operator in Elixir - need help understanding it

I’m failing to grasp what the Bitwise module is doing. The documentation doesn’t have much (see for yourself). I have a decent understanding of binary already. Not sure what the &&& is doing though.

Any explanation to help me understand the following code (just a practice problem off Exercism) would be immensely helpful. I have 2 weeks to get off the ground and start an Elixir project at work so trying to learn as much as I can.

defmodule SecretHandshake do
  @doc """
  Determine the actions of a secret handshake based on the binary
  representation of the given `code`.

  If the following bits are set, include the corresponding action in your list
  of commands, in order from lowest to highest.

  1 = wink
  10 = double blink
  100 = close your eyes
  1000 = jump

  10000 = Reverse the order of the operations in the secret handshake
  """

  use Bitwise

  @spec commands(code :: integer) :: list(String.t())
  def commands(code) do
    []
    |> handshake(code &&& 0b00001)
    |> handshake(code &&& 0b00010)
    |> handshake(code &&& 0b00100)
    |> handshake(code &&& 0b01000)
    |> handshake(code &&& 0b10000)
  end

  def handshake(list, 0b00001), do: list ++ ["wink"]
  def handshake(list, 0b00010), do: list ++ ["double blink"]
  def handshake(list, 0b00100), do: list ++ ["close your eyes"]
  def handshake(list, 0b01000), do: list ++ ["jump"]
  def handshake(list, 0b10000), do: Enum.reverse(list)
  def handshake(list, _), do: list
end

Most Liked

cmkarlsson

cmkarlsson

I find it easier to show it using bits

5 = 0b0101
6 = 0b0110

The Bitwise.band (&&&) is a bitwise and, meaning only if the bit is set on both sides it is kept.

5 = 0b0101
6 = 0b0110
# The position with both bit sets (1) results in the position being set (1).
4 = 0b0100 = 0b0110 &&& 0b0101

The ||| is the bitwise or where you get a 1 if there is a bit set in the same position on either or both side.
The ^^^is the xor (exclusive or) where you get a 1 only if the bit is set on either side. `

A simple demonstration:

iex(30)> [ 0 ^^^ 1, 1 ^^^ 0, 0 ^^^ 0, 1 ^^^ 1 ]
[1, 1, 0, 0]
iex(31)> [ 0 &&& 1, 1 &&& 0, 0 &&& 0, 1 &&& 1 ]
[0, 0, 0, 1]
iex(32)> [ 0 ||| 1, 1 ||| 0, 0 ||| 0, 1 ||| 1 ]
[1, 1, 0, 1]
13
Post #3
kokolegorille

kokolegorille

You can get binary representation of an integer with Integer.to_string x, 2
&&& is and operation, which copy a bit if it exists in both bit representation of numbers

As an example, 5 &&& 6 returns 4, as expected.

iex> Integer.to_string 5, 2
"101"
iex> Integer.to_string 6, 2
"110"
iex> Integer.to_string 4, 2
"100"
iex> use Bitwise
Bitwise
iex> 5 &&& 6
4

BTW if You have a binary like this

0b01101

it will returns a list like that

["wink", "close your eyes", "jump"]
cmkarlsson

cmkarlsson

If a bit is set, it is 1. Otherwise 0.

In this case 1, 10, 100, 1000, 10000 are not decimal numbers. They are the bit position. If a bit is set you should include the specific action.

0b00001 = 'wink'
0b00010 = 'double blink'
0b00100 = 'close your eyes'
0b01000 = 'jump'
0b10000 = 'reverse'

This is a common way to deal with flags in a binary format.

Above you have 5 bits (0-31).

Lets take decimal number 10. This is 0b01010. The bits at position 2 and 4 are set. Which means double-wink and jump

So. you start by checking if the wink bit is set, and then go through all the other commands

I’ll do this imperatively.

## This is not good elixir! Don't do this
commands = []
commands = if (10 &&& 0b00001) == 1, do: commands ++ ["wink"], else: commands
commands = if (10 &&& 0b00010) == 2, do: commands ++ ["double blink"], else: commands
commands = if (10 &&& 0b00100) == 4, do: commands ++ [ "close your eyes"], else: commands
commands = if (10 &&& 0b01000) == 8, do: commands ++ ["jump"], else: commands
commands = if (10 &&& 0b10000) == 16, do: Enum.reverse(commands), else: commands

Basicially we check each individual bit to see if it is set with the Bitwise.&&& operator. If it is set we add the command the the command list or reverse it in case the most significant bit it set.

Your initial solution uses elixir pipes and function pattern matching to do the same thing.

NobbZ

NobbZ

There isn’t actually a need for bit operations here, in my opinoin they make the solution even hard to maintain…

Just a simple list of “steps”, mod/2, div/2 and a recursive function and you are ready to go.

This is my helper function:

defp commands(code, acc, []) when rem(code, 2) === 0, do: Enum.reverse(acc)
defp commands(_,    acc, []), do: acc
defp commands(code, acc, [h|t]) when rem(code, 2) === 1, do: commands(div(code, 2), [h|acc], t)
defp commands(code, acc, [h|t]), do: commands(div(code, 2), acc, t)

The first argument (code) is the number originally passed into the public function, which gets constantly divided by 2 on each recursion.

The second (acc) contains the individual steps of the final handshake.

The third argument is the initial list of steps, for which step I check individually by checking if its evenly divisble by 2. If its not, I add the current “step” to the final handshake.

"reverse" though is not part of the list of commands in this implementation. Here it is assumed, that we will always reverse the handshake when there is no step left to check for, but we still have the current number not beeing even. Though as a small side effect of the implementation of building the list of commands in reverse to not need to use ++/2 for well known reasons, I actually reverse when the “code” wants me to have the handshake forward and I do nothing, when the result is beeing expected as reversed.

zkessin

zkessin

Don’t forget you can also pattern match on bits, and that is often easier to read and understand.

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New

Other popular topics Top

JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New

We're in Beta

About us Mission Statement