前の章では、関数を宣言する方法と、それらをモジュールに結合する方法を検討しました。 この章では、関数の構文をさらに詳しく調べます。
チャプターリスト
この章のソースコードはこちらです。
パターンマッチング
まず、ユーザーに挨拶する関数を作成してみましょう。挨拶テキストは性別によって異なります。 擬似コードの形式では、関数は次のようになります。
function greet(Gender,Name) if Gender == male then print("Hello, Mr. %s!", Name) else if Gender == female then print("Hello, Mrs. %s!", Name) else print("Hello, %s!", Name) end
古典的な
if then else
代わりにパターンマッチングを使用すると、大量の定型コードを保存できます。 これは、パターンマッチングを使用する場合、この関数がErlangでどのように見えるかです。
greet(male, Name) -> io:format("Hello, Mr. ~s!", [Name]); greet(female, Name) -> io:format("Hello, Mrs. ~s!", [Name]).
io:format()
関数は、端末へのフォーマットされた出力に使用されます。 ここでは、関数の引数リストの説明でパターンマッチングを使用しました。 これにより、入力値を同時に割り当て、実行する機能の一部を選択できました。 同時に値を割り当て、「より宣言的な」スタイルで実行できる場合、関数の本体で値を比較するのはなぜですか?
一般的に、このような関数の宣言は次のとおりです。
fnct_name(X) -> Extpression; fnct_name(Y) -> Expression; fnct_name(_) -> Expression.
関数の各ブランチは、本格的な関数として宣言されていますが、セミコロン(
;
)で終わり、次の関数が続きます。 最後の部分の後、ピリオドが置かれます。
最後のサンプルに注意してください。
greet()
関数を呼び出して予期しない性別を指定するとどうなりますか? 入力パラメーターがどのサンプルにも適合しないという例外が発生します。
1> chapter03:greet(someOther, "Haru"). ** exception error: no function clause matching chapter03:greet(someOther, "Haru")
したがって、任意の値に一致するサンプルを広告に含めることが重要です。 しかし、それは最後でなければなりません。 それ以外の場合、サンプルが発表された後に処理されることはありません。
関数を書き直して、無効な入力値を正しく処理するようにしましょう。
greet(male, Name) -> io:format("Hello, Mr. ~s!", [Name]); greet(female, Name) -> io:format("Hello, Mrs. ~s!", [Name]); greet(_, Name) -> io:format("Hello, ~s!", [Name]).
ただし、関数宣言でのパターンマッチングは、単にコードを減らすよりもはるかに便利です。 リストを思い出してください:リストは頭と残りで構成されています。 結果リストの最初と2番目の要素を返す2つの関数を書きましょう。
first([X|_]) -> X. second([_,X|_) -> X.
簡単ですよね? Erlangの変数は一度しか割り当てられないという事実に基づいた別の興味深いトリックがあります。
same(X,X) -> true; same(_,_) -> false.
この関数は、引数が同じ場合は
true
返し、そうでない場合は
false
返し
false
。 どのように機能しますか? 関数
chapter03::same(one, two).
呼び出すとき
chapter03::same(one, two).
X
値
one
X
割り当てられ、値
two
を同じ変数に割り当てようとします。 値がすでに割り当てられているという事実により、試行は失敗し、テンプレートは不適切なものとして破棄されます。 2番目のケースでは、値を割り当てる変数を明示的に指定しないため、テンプレートが適切であり、関数は
false
返し
false
。
chapter03:same(3, 3).
同じ値を渡す場合
chapter03:same(3, 3).
、最初のテンプレートが機能し、関数は
true
を返し
true
。
セキュリティ表現
サンプルとの比較には、大きな欠点が1つあります。表現力が不十分です。 これを使用すると、データ型、範囲、およびその他の同様の一般化を指定できません。 各サンプルは特定のケースです。 Erlangには、この問題を解決するためのセキュリティ表現(またはウォッチドッグ条件)があります。 はじめに、BMI(body mass index)を取得して条件を評価する関数を作成しましょう。 人のBMIは、体重を成長の2乗で割った値に等しくなります。
bmi_tell(Bmi) when Bmi =< 18.5 -> "You're underweight."; bmi_tell(Bmi) when Bmi =< 25 -> "You're supposedly normal."; bmi_tell(Bmi) when Bmi =< 30 -> "You're fat."; bmi_tell(_) -> "You're very fat.".
ここでは、関数にアクセスするときに、単語
when
(
Bmi =< 18.5
)の後に最初の条件がチェックされます。 この式が
true
返す
true
、対応するコードブランチが実行されます。 そうでない場合は、次の条件がチェックされ、最後まで同様にチェックされます。 そしてここで、最後に、値が適合する条件を追加しました。
一般に、ガード式を使用した関数宣言は次のとおりです。
fnct_name(Arg_1, Arg_1, ..., Arg_n) when rule_1 -> Expression; fnct_name(Arg_1, Arg_1, ..., Arg_n) when rule_2 -> Expression.
また、条件内の1つの式に限定されません。 いくつかのチェックを行うことができます。 テストに合格するために両方の条件(analogueおよび
andalso
)を満たす必要がある場合、それらの間にコンマ(
,
)が挿入されます。
lucky_number(X) when 10 < X, X > 20 -> true; lucky_number(_) -> false.
少なくとも1つ(
orelse
類似)で
orelse
場合、セミコロン(
;
)で区切られ
;
。
lucky_atom(X) when X == atom1; X == anot2 -> true; lucky_atom(_) -> false.
セキュリティ式で関数を使用できます。 ある数値を別の数値に分割する関数を次に示しますが、その前に、渡された引数が数値であり、
Y
ゼロでないことを確認します。
safe_division(X, Y) when is_integer(X), is_integer(Y), Y /= 0 -> X / Y; safe_division(_, _) -> false.
分岐
if
上記の式に加えて、Erlangには通常の
if
ます。 この演算子を使用して
bmi_tell()
関数を書き直しましょう。
if_bmi_tell(Bmi) -> if Bmi =< 18.5 -> "You're underweight."; Bmi =< 25 -> "You're supposedly normal."; Bmi =< 30 -> "You're fat."; true -> "You're very fat." end.
一般的な場合、
if
branch
if
は次のようになります。
if Rule_1 -> Expression; Rule_2 -> Expression; ... true -> Expression end.
Haskellとは異なり、ここのインデントは装飾以外の役割を果たさず、コードは認識しやすいようにフォーマットされていることを思い出してください。 自由にフォーマットできます。
ここで、式
rule_1
および
rule_2
は1つ以上の条件です。 条件が正常に満たされた場合、それに続く
Expression
コードブロック(1つまたは複数のコマンドが含まれる場合があります)が実行されます。 このような論理分岐はいくつあってもかまいません。
最後のブロックに注意して
true -> "You're very fat.
orelse
true -> "You're very fat.
この条件は何ですか?これは
orelse
演算子の代替です。テストに合格する条件がない場合、それに続くコードブロックが実行されます。覚えているように、Erlangでは、どの式も結果を返す必要があります。このブロックが記述されていない場合、モジュールはコンパイルされますが、実行スレッドがすべての条件をチェックし、
true
ブロックを見つけられない場合、例外がスローされます。
** exception error: no true branch found when evaluating an if expression in function chapter03:if_bmi_tell/1
case ... of
if
に加えて、Erlangには別の分岐演算子が
case of
ます。 そして、この演算子は別の関数に挿入された関数全体のようなものです。 条件に基づいて選択するだけでなく、パターンおよびセキュリティ式との比較を使用することもできます。 一般的には、次のようになります。
case Rule of Val_1 -> Expression; Val_2 -> Expression ... Val_n -> Expression
ここで、
Rule
は、結果がチェックされる変数または式です。 これに、馴染みのある条件式ブロックが続きます(
Val_1 -> Expression;
)。 条件では、パターンマッチングとガード式を使用できます。これにより、この設計は非常に柔軟になります。
たとえば、温度とその測定スケールの名前で構成されるタプルを取得し、これらのデータに基づいて評価する関数を作成してみましょう。
assessment_of_temp(Temp) -> case Temp of {X, celsius} when 20 =< X, X =< 45 -> 'favorable'; {X, kelvin} when 293 =< X, X =< 318 -> 'scientifically favorable'; {X, fahrenheit} when 68 =< X, X =< 113 -> 'favorable in the US'; _ -> 'not the best tempperature' end.
この関数が実行されると、タプルが
Temp
変数にマップされ、このタプルが適合するパターンが検出されます。 その後、セキュリティ式を使用してチェックが実行され、合格すると、関数は対応する行を返します。
ここで、前と同様に、不適切なオプションをすべてインターセプトする式を最後に追加します。
ご覧のとおり
case of
式の
case of
ほぼ完全に関数宣言領域に移動できます。 では、条件を設定するのに適した場所はどこですか? 答えは簡単です:あなたが一番好きな場所。 2つのデザインの違いは最小限です。 したがって、読みやすいオプションを使用してください。
おわりに
この章では、関数内で実行のフローを制御する方法について説明しました。 このためにどのようなデザインが存在し、どのように使用するかを学びました。 また、セキュリティ表現に精通しました。
次の章では、Erlangの型システムについて詳しく見ていきます。
最後まで読んでくれてありがとう。 面白かったと思います。 このような長い遅延をおaび申し上げます。 暇はありません。
PS私は個人的にタイプミスやエラーについて通知するようにお願いしますが、コメントではお知らせしません。 ご理解いただきありがとうございます。