この記事は「Elixir Advent Calendar 2023」25日目の記事です
東京にいるけどfukuokaexのYOSUKEです。
普段は 合同会社TheWaggle で教材開発・システム開発・研修講師などマルチに活動してます。
このシリーズでは、文系ですらない、体育会系でも始められるプログラミング学習 というテーマで元自衛官だった僕が
自衛官からエンジニアになってもらいたいという気持ちを込めて、普段は大人しいですが、このコラムシリーズでは、あえて、体育会系のノリで、貴様ら理屈で覚えるなら体で覚えろ!というノリで書いてみたいと思います。
なので、初学者向けですが、理論は後回し、まずは動かして体感しろってノリの第25回目です。
それでは、いくぞ!
隠し事はプライベートで
よーし、貴様ら 前回までの知識を総動員していよいよ仕上げをしていくぞ、前回までの知識をフル活用した関数が以下だ、そしてこの関数に仕上げをしていく。
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
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)
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)
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)
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)
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
最後にこのcreate_c4
関数は材料を入れて、正しければ "c-4"
を返す関数にする必要がある。
今回は、話しを簡単にする為に、必ず[45.5, 10.5, 0.8, 2.65, 25]
これか、[91, 0.1, 5.3, 2.1, 1.5]
これか、[91, 5.3, 2.1, 1.5]
この値のいずれかの場合にのみc-4
が作れると決める。
そこで、入ってきた値が正しいかどうか判定する関数を作る。要するに、[45.5, 10.5, 0.8, 2.65, 25]
or [91, 0.1, 5.3, 2.1, 1.5]
or [91, 5.3, 2.1, 1.5]
この時に true を返し、それ以外はfalse
を返す関数だ。
値が正しいか判定する関数だから名前はis_value_corect?()
という名前にする。そして、この関数は、create_c4
の関数の中でのみ利用する関数で他の物には秘密にしておきたい。そのため、プライベートを表すp
を関数の定義につけてdefp
として定義する。
defpとは
いいか、defp
とは、まるで軍の機密情報のようなものだ。外部からはアクセスできない、モジュール内部でのみ使われる情報や機能だ。
Elixirでは、defキーワードを使って公開関数(public functions)を定義する。これらの関数はモジュールの外部から呼び出すことができる。一方で、defpキーワードを使って非公開関数(private functions)を定義する。これらの関数は、定義されたモジュール内部でのみ使うことができ、モジュールの外部からは呼び出すことができない。
非公開関数は、モジュールの実装の詳細を隠蔽するために使用される。これにより、モジュールの外部に公開するインターフェースを清潔に保ち、モジュールの内部実装を自由に変更できるようになる。まるで、部隊の作戦計画を隠して、敵に知られないようにするようなものだ。
そして、コードの安全性と整理を保つための戦略的な決断だ。内部の詳細を隠し、公開する部分だけをきちんとコントロールするよう心がけろ! わかったな。
is_value_corect?()
の実装にはcase
を使え
貴様ら! いいか is_value_correct?
関数を使って判定処理を今回はcase文を使って行う。case文は、複数の異なるパターンに基づいて、異なる動作をする分岐を作成するために使用される。これは、戦場での状況判断に似ている。敵の動きに応じて、即座に適切な対応を取るんだ。
今回のis_value_correct?
関数では、引数として与えられたリストが特定のパターンに一致するかどうかをチェックしている。具体的な値が一致する場合にはtrueを返し、それ以外の場合にはfalseを返す。という具合だ。
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
具体的な動きは以下の通りだ。
- listが[45.5, 10.5, 0.8, 2.65, 25]というパターンに一致する場合、trueを返す。
- listが[91, 0.1, 5.3, 2.1, 1.5]というパターンに一致する場合、trueを返す。
- listが[91, 5.3, 2.1, 1.5]というパターンに一致する場合、trueを返す。
- 上記のいずれにも一致しない場合(
_
を使用)、falseを返す。
このcase文の利用は、まるで複数の敵軍の動きを予測して、それぞれに適した戦術を選択するようなものだ。さまざまな可能性を考慮に入れ、それぞれに対して最適な対応をする。それがcase文の役割だ。
しかし、貴様ら、注意が必要だ。今回は特別に話しをシンプルにするためにリテラルの値をそのままチェックする作りにしたが、通常はあまり推奨されない
。
なぜなら、コードの柔軟性が低く、保守が難しくなるからだ。実際の戦場では、状況は常に変化する。だから、コードも柔軟に対応できるよう設計する必要がある。理解したか?
よし、話しが長くなるから今回はここまで、次回 true
,false
から "c-4"
生成を追加していく。
今日はここまで、それでは次回 Part26でまた会おう!