Elixir 学習記（3. 演算子）

# Elixir学習記

| ← 前 | [目次](https://qiita.com/SF-28/items/197df5e03ab9787f40a0) | 次 → |
| :-: | :-: | :-: |
| [2. データ型](https://qiita.com/SF-28/items/3f4208d51f2bbaa4afe9) | 3. 演算子 | [4. パターンマッチング](https://qiita.com/SF-28/items/c82793814f5493e51b69) |

# 演算子

## 算術演算

### 加算 `+`

```elixir
6 + 2 # == 8
```

### 減算 `-`

```elixir
6 - 2 # == 4
```

### 乗算 `*`

```elixir
6 * 2 # == 12
```

### 除算 `/`

```elixir
6 / 2 # === 3.0 (割り切れる場合でも Float になる)
```

### 整数の除算 `div` `rem`

```elixir
# 商
div(6, 2) # == 3

# 剰余
rem(6, 2) # == 0
```

## 論理演算

- `and`, `or`, `not` は Guard であり、第1引数が `true` か `false` でないとエラーになる
- `&&`, `||`, `!` は Function であり、どんなデータ型でもOK
    - `false`, `nil` のみ偽と判定される

Guard、Functionという違いについて教えていただきました -> [@zacky1972さんのコメント](https://qiita.com/SF-28/items/82dfb7cd5398656adbac#comment-ba6dce6e4f2528e276e2)

### 論理積 `and` `&&`

```elixir
true and false # == false
1 && nil # == nil
```

- `and`
    - 第1引数が`false`なら第1引数(`false`)を返す
    - 第1引数が`true`なら第2引数を返す
- `&&`
    - 第1引数が`false`か`nil`なら第1引数(`false`か`nil`)を返す
    - 第1引数がそれ以外なら第2引数を返す

### 論理和 `or` `||`

```elixir
false or true # == true
nil || 1 # == 1
```

- `or`
    - 第1引数が`false`なら第2引数を返す
    - 第1引数が`true`なら第1引数(`true`)を返す
- `||`
    - 第1引数が`false`か`nil`なら第2引数を返す
    - 第1引数がそれ以外なら、第1引数を返す

### 否定 `not` `!`

```elixir
not true # == false
!1 # == false
```

- `not`
    - `true`なら`false`を返す
    - `false`なら`true`を返す
- `!`
    - `false`か`nil`なら`true`を返す
    - それ以外なら`false`を返す

## 比較演算

### equal (等しい) `==` `===`

```elixir
# 緩い比較(型が違っても同じ値と見做される)
1 == 1 # true
1 == 2 # false
1 == 1.0 # true

# 厳密な比較(型が違う＝異なる値と見做される)
1 === 1 # true
1 === 1.0 # false
```

### not equal (等しくない) `!=` `!==`

```elixir
# 緩い比較
1 != 1 # false
1 != 2 # true
1 != 1.0 # false

# 厳密な比較
1 !== 1 # false
1 !== 2 # true
1 !== 1.0 # true 
```

### greater than (より大きい) `>`

```elixir
2 > 1 # true
2 > 2 # false
```

### less than (より小さい) `<`

```elixir
2 < 1 # false
2 < 2 # false
```

### greater than or equal (以上) `>=`

```elixir
2 >= 1 # true
2 >= 2 # true
```

### less than or equal (以下) `<=`

``` elixir
2 <= 1 # false
2 <= 2 # true
```

### 参考： 異なる型の比較

- 型により大小関係が決まっている
- 異なる型の要素が含まれるListのソートなどで関係してくる

```text
number < atom < reference < function < port < pid < tuple < map < list < bitstring
```

```elixir
100 < [10] # == true
[10] < "1" # == true
```

## ビット演算

```elixir
import Bitwise

a = 12 # 0b1100
b = 10 # 0b1010 
```

### AND `&&&` `band/2`

```elixir
a &&& b
band(a, b)

# == 8 (0b1000) 

# 補足
# a &&& b
# |> IO.inspect(base: :binary)
# とすればバイナリ表現を得ることができる
```

### OR `|||` `bor/2`

```elixir
a ||| b
bor(a, b)

# == 14 (0b1110)
```

### NOT `~~~` `bnot/1`

```elixir
~~~a
bnot(a)
# == -13 (-0b1101)

# 12 (00...001100) の反転なら、 (11...110011) になるはずだが、そうなっていない?
# 負の整数は2の補数で表現されることが多いが、Elixirのバイナリ表示は -(マイナス)と整数部の組み合わせになっている?
# 負の整数が2の補数で表現される場合、ビット反転は a * (-1) - 1 に相当するため、その結果を再現している?
```

### XOR `bxor/2`

```elixir
bxor(a, b)
# == 6 (0b0110)

# ^^^ は deprecated
```

### 算術右シフト `>>>` `bsr/2`

```elixir
a >>> 1
bsr(a, 1)
# == 6 (0b110)

-4 >>> 1 # -4 == -0b100
# == -2 (-0b10)
```

### 算術左シフト `<<<` `bsl/2`

```elixir
a <<< 1
bsl(a, 1)
# == 24 (0b11000)
```

## その他の演算子

### 文字列(バイナリ)連結 `<>`

```elixir
"Hello" <> " " <> "World" # == "Hello World"

<<10>> <> <<20>> # == <<10, 20>>
```

### リスト連結 `++`

```elixir
[1, 2] ++ [3, 4] # == [1, 2, 3, 4] 
```

### マッチ `=`

- 単なる代入演算子ではない
- 詳細はパターンマッチの記事で書く予定

```elixir
x = 1
```

### ピン `^`

- 左辺の変数を値割り当てでなくパターンマッチに使うことができる
- aの値を固定しておくイメージ

```elixir
a = 12 # 割り当て
a = 13 # 割り当て
^a = 13 # パターンマッチング
```

### パイプ `|>`

- 左辺の結果を、右辺に指定した関数の第1引数として渡す
- 処理の流れがわかりやすく、可読性向上に役立つ

```elixir
add = fn a, b -> a + b end
subtract = fn a, b -> a - b end
multiply = fn a, b -> a * b end
divide = fn a, b -> a / b end

add.(10, 5)     # == 15
|> subtract.(3) # == 12
|> multiply.(2) # == 24
|> divide.(4)   # == 6.0
```

参考：上記をパイプなしにした場合

```elixir
divide.(multiply.(subtract.(add.(10, 5), 3), 2), 4)
```
非常に読みづらい:sob:

### 文字列マッチ `=~`

- 左辺に文字列、右辺に文字列or正規表現を取る
- 右辺が文字列の場合
    - 右辺が左辺に含まれる(左辺 ⊇ 右辺)場合、`true`　となる
- 右辺が正規表現の場合
    - 正規表現にマッチする場合、`true`となる

```elixir
"tutorialspoint" =~ "poi" # == true
"tutorialspoint" =~ "poi2" # == false

"tutorialspoint" =~ ~r/[a-z]+/ # == true
"tutorialspoint" =~ ~r/[A-Z]+/ # == false
```

### コードポイント `?`

UTF-8 コードポイントを返す

```
?a # == 97
?\s # == 32
```

### キャプチャ `&`

無名関数定義で使う(関数の記事でまた触れる予定)

```elixir
&(&1 + &2)

# fn a, b -> a + b end に相当
# == で比較すると false になったので完全に同じではないみたい
```

### 三項

三項演算子はないため、if - do - else で実現する

```elixir
if true, do: "True!", else: "False!" # == "True!"
```

### イン `in`

```elixir
:yes in [:true, :false, :yes] # == true
```

# 参考資料

- [Elixir 公式ガイド](https://elixir-lang.jp/getting-started/introduction.html)
    - [基本演算子](https://elixir-lang.jp/getting-started/basic-operators.html)
- [Elixir Tutorial](https://www.tutorialspoint.com/elixir/index.htm)
    - [Elixir - Operators](https://www.tutorialspoint.com/elixir/elixir_operators.htm)
- [Elixir School](https://elixirschool.com/ja)
    - [基本](https://elixirschool.com/ja/lessons/basics/basics)

# 感想

- 論理演算の論理積、論理和について
    - Boolean(`true`か`false`)でなく第1引数、第2引数を返す仕様なのは面白いと思った
- ビット演算について
    - Integerでなく、BitString(もしくはせめてBinaries)を対象として計算できるほうが便利そうだけど、どうやるんだろう？
       -> [@mnishiguchiさんのコメント](https://qiita.com/SF-28/items/82dfb7cd5398656adbac#comment-0ff7dcce460e8bb65da5)が参考になります！