自衛官からエンジニアへ Elixirから始める Web開発 Part24

この記事は「Elixir Advent Calendar 2023」24日目の記事です
東京にいるけどfukuokaexのYOSUKEです。

普段は 合同会社TheWaggle で教材開発・システム開発・研修講師などマルチに活動してます。

このシリーズでは、文系ですらない、体育会系でも始められるプログラミング学習　というテーマで元自衛官だった僕が
自衛官からエンジニアになってもらいたいという気持ちを込めて、普段は大人しいですが、このコラムシリーズでは、あえて、体育会系のノリで、貴様ら理屈で覚えるなら体で覚えろ！というノリで書いてみたいと思います。

なので、初学者向けですが、理論は後回し、まずは動かして体感しろってノリの第24回目です。

それでは、いくぞ！

## c4爆弾の作成をさらに守るガード句

よーし、貴様ら、前回までで、使いやすいc4爆弾を生成する関数を作ってきたが、値を文字列で取得した場合は処理するが、値を数値で受け取った場合にはエラーになる。

しかし！ 数値 で入ってきた場合にも正常に処理する必要がある。そこで、さらに引数の値でパターンマッチを振り分ける方法`ガード句`を伝授する

以下のパターンの関数について、値が文字列と値が数値で入ってきたパターンのガード句を追加したものを見せよう。

```elixir
create_c4({"RDX", rdx}, {"ポリイソブチレン", polyisobutylene}, {"モーターオイル", motorOil}, {"セバシン酸ジオクチル", dioctylSebacate}, {"ガソリン", gasoline}) 
```

以下に

```elixir
defmodule TNT do
  def create_c4({"RDX", rdx}, 
                {"ポリイソブチレン", polyisobutylene}, 
                {"モーターオイル", motorOil}, 
                {"セバシン酸ジオクチル", dioctylSebacate}, 
                {"ガソリン", gasoline}) when is_binary(rdx) 
                and is_binary(polyisobutylene) 
                and is_binary(motorOil) 
                and is_binary(dioctylSebacate) 
                and is_binary(gasoline) do

      materials = [rdx, polyisobutylene, motorOil, dioctylSebacate, gasoline]
      Enum.map(materials, &convert_material_to_numeric/1)
  
  end

  def create_c4({"RDX", rdx}, 
                {"ポリイソブチレン", polyisobutylene}, 
                {"モーターオイル", motorOil}, 
                {"セバシン酸ジオクチル", dioctylSebacate}, 
                {"ガソリン", gasoline}) when is_number(rdx) and rdx > 0 
                and is_number(polyisobutylene) and polyisobutylene > 0 
                and is_number(motorOil) and motorOil > 0 
                and is_number(dioctylSebacate) and dioctylSebacate > 0 
                and is_number(gasoline) and gasoline > 0 do
            
        materials = [rdx, polyisobutylene, motorOil, dioctylSebacate, gasoline]
        Enum.map(materials, & &1)
  end

  defp convert_material_to_numeric(material) do
    numeric_part = String.replace(material, ~r/[gml%]/, "")
    
    convert_to_numeric(numeric_part)
  end

  defp convert_to_numeric(numeric_part) do
    if String.contains?(numeric_part, ".") do
      String.to_float(numeric_part)
    else
      String.to_integer(numeric_part)
    end
  end
end
```

このように記載することで以下のどちらでも実行できるようになる。

```elixir
TNT.create_c4({"RDX", "45.5g"}, {"ポリイソブチレン", "10.5g"}, {"モーターオイル", "0.8g"}, {"セバシン酸ジオクチル", "2.65g"}, {"ガソリン", "25ml"})
[45.5, 10.5, 0.8, 2.65, 25]

TNT.create_c4({"RDX", 45.5}, {"ポリイソブチレン", 10.5}, {"モーターオイル", 0.8}, {"セバシン酸ジオクチル", 2.65}, {"ガソリン", 25})
[45.5, 10.5, 0.8, 2.65, 25]
```

何？ ガード句の意味がわからないだと！　

いいだろう。貴様らのそのnil頭に叩き込んでやるからよーく聞け！

`when`キーワードを使用したガード句について解説してやろう。これは、まるで戦場での状況判断のようなものだ。敵がどこにいるのか、どう動くかを予測し、それに応じて行動を決める。それがガード句だ。

Elixirの`when`キーワードは、特定の関数やマクロが呼び出される条件を指定するために使う。これは、関数に渡される引数が特定の条件を満たす場合にのみ、その関数を実行させるためのものだ。まるで、特定の条件下でのみ発火する爆薬のようなもの。

たとえば、数値が特定の範囲内にあるかどうか、引数が特定の型であるかどうかなど、様々な条件をガード句でチェックすることができる。

貴様らのようなnil頭でも理解しやすいように簡単な例を出してやろう
```elixir 
defmodule Example do
  def check_number(x) when is_integer(x) and x > 0 do
    IO.puts("#{x}は正の整数です")
  end

  def check_number(x) when is_float(x) and x < 0 do
    IO.puts("#{x}は負の浮動小数点数です")
  end
end
```
この例では、check_number 関数に2つのパターンがあり、それぞれ異なるガード句が定義されている。一つ目の関数は、引数xが整数であり、かつ0より大きい場合にのみ実行される。二つ目の関数は、xが浮動小数点数であり、かつ0未満の場合にのみ実行される。

このように、ガード句は関数の動作をより精密にコントロールするための強力なツールだ。それは、戦場での戦略やタクティクスを練るようなもの。状況に応じて適切な対応を取ることで、任務を成功に導く。Elixirのガード句も同じだ。正しい状況でのみ関数を実行させることで、コードの安全性と効率を高めるんだ。

わかったか、貴様ら！ガード句はただの条件分岐ではない。それは、コードの安全と精度を高めるための戦略的な決断だ！

今日はここまで、それでは次回 Part25でまた会おう！