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

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

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

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

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

それでは、いくぞ！

### `result!()`何もしない、何もしたくない。

よーし、貴様ら今日も"c-4"爆弾を作成していくぞ！　今回は、前回の続きだ。前回までの内容を作ったのがこの関数だ！

```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)  
                  |> is_value_corect?
  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)
                  |> is_value_corect?

  end

  def create_c4({"RDX", rdx}, 
                {"ジメチルジニトロブタン", dimethyldinitrobutane}, 
                {"可塑剤", plasticizer}, 
                {"結合剤", binder}, 
                {"界面活性剤", surfactant}) when
                is_binary(rdx) and
                is_binary(dimethyldinitrobutane) and
                is_binary(plasticizer) and 
                is_binary(binder) and
                is_binary(surfactant) do

                  materials = [rdx, dimethyldinitrobutane, plasticizer, binder, surfactant]
                  Enum.map(materials, &convert_material_to_numeric/1)
                  |> is_value_corect?
  end

  def create_c4({"RDX", rdx}, 
                {"ジメチルジニトロブタン", dimethyldinitrobutane}, 
                {"可塑剤", plasticizer}, 
                {"結合剤", binder}, 
                {"界面活性剤", surfactant}) when
                is_number(rdx) and rdx > 0 and
                is_number(dimethyldinitrobutane) and dimethyldinitrobutane > 0 and
                is_number(plasticizer) and plasticizer > 0 and
                is_number(binder) and binder > 0 and
                is_number(surfactant) and surfactant > 0 do

                  materials = [rdx, dimethyldinitrobutane, plasticizer, binder, surfactant]
                  Enum.map(materials, & &1)
                  |> is_value_corect?
  end

  def create_c4({"RDX", rdx}, 
                {"可塑剤", plasticizer}, 
                {"結合剤", binder}, 
                {"界面活性剤", surfactant}) when
                is_binary(rdx) and
                is_binary(plasticizer) and 
                is_binary(binder) and
                is_binary(surfactant) do

                  materials = [rdx, plasticizer, binder, surfactant]
                  Enum.map(materials, &convert_material_to_numeric/1)
                  |> is_value_corect?
  end

  def create_c4({"RDX", rdx}, 
                {"可塑剤", plasticizer}, 
                {"結合剤", binder}, 
                {"界面活性剤", surfactant}) when
                is_number(rdx) and rdx > 0 and
                is_number(plasticizer) and plasticizer > 0 and
                is_number(binder) and binder > 0 and
                is_number(surfactant) and surfactant > 0 do

                  materials = [rdx, plasticizer, binder, surfactant]
                  Enum.map(materials, & &1)
                  |> is_value_corect?
  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

  defp is_value_corect?(list) do
    case list do
        [45.5, 10.5, 0.8, 2.65, 25] -> true
        [91, 0.1, 5.3, 2.1, 1.5] -> true
        [91, 5.3, 2.1, 1.5] ->  true
        _ -> false
    end
  end
end
```
しかし、このままでは、結果は true か falseが返ってくるだけだ、我々が作りたいのは"c-4"だ、そして、もし仮に素材の分量が間違えていたら注意をする必要がある。"分量が違います"と！

そこで、最後に判定が正しければ "c-4"を返す関数を作る。そして判定がfalseだった場合は"材料が違います"と返すようにしよう。しかし、true, false 以外が入ってきた場合には何もせずに マッチエラーを返す。

```elixir
defp result!(true), do: "c-4"
defp result!(false), do: "分量が違います"
```

何？ 貴様ら、これは関数か？だと、立派な関数だ。ただ書き方がシンプルなだけだ。
よーし、nil頭の貴様らに、この関数について説明してやろう。

今回の示した```defp result!(true), do: "c-4"```このように関数の本体が単一の式の場合、より簡潔な構文を使って関数を定義できる。これは、戦場での迅速かつ効果的な指示のようなものだ。無駄を省き、目的に直行する。

result!関数がtrueまたはfalseを受け取り、それに応じて異なる文字列を返す。この関数は非常にシンプルで、その結果は引数に直接依存している。このような場合、Elixirでは一行で関数を記述することができる。これを「インライン関数定義」と呼ぶ。

これは、関数本体が単一の式である場合に適している。この方法はコードを簡潔にし、意図を明確にする。まるで、短いが明確な命令で兵士を動かす指揮官のようだ。

しかし、貴様ら、この方法は複雑なロジックや複数のステップを含む関数には向いていない。そういう場合は、通常の複数行の関数定義が適切だ。目的に合わせて、適切な形式を選ぶことが重要だ。

わかったか、貴様ら！このインライン関数定義は、コードを簡潔に保ちつつ、意図を明確にするための戦術だ。

それでは今までの内容を全て詰め込んだコードが以下だ。

```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)  
                  |> is_value_corect?
                  |> result!
  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)
                  |> is_value_corect?
                  |> result!

  end

  def create_c4({"RDX", rdx}, 
                {"ジメチルジニトロブタン", dimethyldinitrobutane}, 
                {"可塑剤", plasticizer}, 
                {"結合剤", binder}, 
                {"界面活性剤", surfactant}) when
                is_binary(rdx) and
                is_binary(dimethyldinitrobutane) and
                is_binary(plasticizer) and 
                is_binary(binder) and
                is_binary(surfactant) do

                  materials = [rdx, dimethyldinitrobutane, plasticizer, binder, surfactant]
                  Enum.map(materials, &convert_material_to_numeric/1)
                  |> is_value_corect?
                  |> result!
  end

  def create_c4({"RDX", rdx}, 
                {"ジメチルジニトロブタン", dimethyldinitrobutane}, 
                {"可塑剤", plasticizer}, 
                {"結合剤", binder}, 
                {"界面活性剤", surfactant}) when
                is_number(rdx) and rdx > 0 and
                is_number(dimethyldinitrobutane) and dimethyldinitrobutane > 0 and
                is_number(plasticizer) and plasticizer > 0 and
                is_number(binder) and binder > 0 and
                is_number(surfactant) and surfactant > 0 do

                  materials = [rdx, dimethyldinitrobutane, plasticizer, binder, surfactant]
                  Enum.map(materials, & &1)
                  |> is_value_corect?
                  |> result!
  end

  def create_c4({"RDX", rdx}, 
                {"可塑剤", plasticizer}, 
                {"結合剤", binder}, 
                {"界面活性剤", surfactant}) when
                is_binary(rdx) and
                is_binary(plasticizer) and 
                is_binary(binder) and
                is_binary(surfactant) do

                  materials = [rdx, plasticizer, binder, surfactant]
                  Enum.map(materials, &convert_material_to_numeric/1)
                  |> is_value_corect?
                  |> result!
  end

  def create_c4({"RDX", rdx}, 
                {"可塑剤", plasticizer}, 
                {"結合剤", binder}, 
                {"界面活性剤", surfactant}) when
                is_number(rdx) and rdx > 0 and
                is_number(plasticizer) and plasticizer > 0 and
                is_number(binder) and binder > 0 and
                is_number(surfactant) and surfactant > 0 do

                  materials = [rdx, plasticizer, binder, surfactant]
                  Enum.map(materials, & &1)
                  |> is_value_corect?
                  |> result!
  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

  defp is_value_corect?(list) do
    case list do
        [45.5, 10.5, 0.8, 2.65, 25] -> true
        [91, 0.1, 5.3, 2.1, 1.5] -> true
        [91, 5.3, 2.1, 1.5] ->  true
        _ -> false
    end
  end

  defp result!(true), do: "c-4"
  defp result!(false), do: "分量が違います"
end
```

これを様々な呼び出しで実行してみよう。

```elixir
TNT.create_c4({"RDX", "92%"}, {"ジメチルジニトロブタン", "0.1%"},  {"可塑剤", "5.3%"}, {"結合剤", "2.1%"}, {"界面活性剤", "1.5%"})
"分量が違います"
```

```elixir
TNT.create_c4({"RDX", "91%"},{"可塑剤", "5.3%"}, {"結合剤", "2.1%"}, {"界面活性剤", "1.5%"})
"c-4"
```

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

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