条件分岐
F#で条件分岐を表すための方法のうちの一つが
条件式(conditional expression)です。
条件式を用いると、「もし〜だったら〜する」という条件ををプログラミングすることができます。
条件式を記述する際には、ifというキーワードを使います。
単純な場合分け
まず、elifを省略した構文(参考:
MSのドキュメント)はこうなります。
- 条件式(conditional expression) elif省略版
- if Boolean-expression then expression1 [ else expression2 ]
([]の部分は省略出来ます)
Boolean-expressionにはbool値を返す式を書きます。
条件式は、まずBoolean-expressionを評価し、その結果がtrueであれば
expression1を評価し、それが条件式の値になります。
Boolean-expressionがfalseであった場合はexpression1は評価されずにexpression2が評価され、
それが条件式の値になります。
条件式は文ではなく式なので、expression1とexpression2は同じ型である必要があります。
もしelse節が省略された場合は、expression2はunit型の値を返すものとされるため(f#4.1 spec 6.5.3 等価なパターンマッチの例より)
expression1の結果も()を返す式になる必要があります。
まず、一番シンプルな例はこんな感じになります。
条件式の例
> if true then 1 else 0;;
val it : int = 1
> if false then 1 else 0;;
val it : int = 0
普通はbool値を返す式の部分には、比較を行う演算子などを使った条件を書くので
条件式の例
> let a = 2;;
val a : int = 2
> if (a=1+1) then "ok" else "ng";;
val it : string = "ok"
> if (a>10) then "over10" else "not over10";;
val it : string = "not over10"
実際に使う際はこのような形になると思います。
=や>などは、演算子の章で出てきた
値の大小比較や、値が等しいかを調べるための演算子です。
elseを省略した際に同じようなことをしようとすると
返値が()でないのでエラー発生
> let a = 2;;
val a : int = 2
> if a=2 then "ok";;
if a=2 then "ok";;
------------^^^^
stdin(28,13): error FS0001: This expression was expected to have type
unit
but here has type
string
結果の型はunit型で無ければならないので
このようにエラーが起きてしまいます。
printfやprintfnの結果はunit型なので
elseの省略
> let a = 2;;
val a : int = 2
> if a = 2 then printfn "ok";;
ok
val it : unit = ()
このようにするとうまくいきます。
どんな結果の型もunit型に変換してくれるignoreを使う方法もあります。
この例ではちょっとナンセンスですが
ignore
> let a = 2;;
val a : int = 2
> if a=2 then ignore("ok");;
val it : unit = ()
こんな感じで使えます。
複数の場合分け
より一般的なF#の条件式の定義は次のようになります(f#4.1 spec 6.5.3)(仕様上の構文はページ下部)
ここで出てくるelifはelse ifを多重に書く代わりに使えるキーワードです。
- conditional expression
- if expr_1a then expr_1b
elif expr_2a then expr_2b
…
elif expr_na then expr_nb
else exprlast
expr_1a ... expr_naにはbool値を返す式を書きます。
この構文は
まずexpr_1a評価し、結果trueであればexpr_1bを評価し、それが条件式の値になります。
さもなければexpr_2aを評価し、結果がtrueならexpr_2bを評価し、それが条件式の値になります。
さもなければ、...と同様に続き
さもなければexpr_naを評価し、結果がtrueならexpr_nbを評価し、それが条件式の値になります。
上記のケースが全てfalseであれば、exprlastが評価され、それが条件式の値になります。
各elifの行とelseの行は省略出来ます。
また、expr_1b〜expr_nbとexprlastの型は同じである必要があります。
次のプログラムはtimeの値によって
異なる挨拶を表示するプログラムで
timeの値を15以外に変更することで
時間にあった挨拶を表示することが出来ます。
複数の場合分け
let time = 15 in
if time < 11 then printfn "Good morning"
elif time < 16 then printfn "Hello"
elif time < 20 then printfn "Good evening"
else printfn "Good night";;
elifを使うと、このように
複数の場合分けを記述できます。
また、elifを使わずに書くことも出来ますが、
ちょっと冗長になってしまいます
複数の場合分け
let time = 15 in
if time < 11 then printfn "Good morning"
else if time < 16 then printfn "Hello"
else if time < 20 then printfn "Good evening"
else printfn "Good night";;
関数との違い
条件式は式なので束縛することが出来ます。
条件式の値を束縛
> let test = if (1+1=2) then "ok" else "ng" in test;;
val it : string = "ok"
CやJavaなどの言語に慣れた人には不思議に見えることと思いますが
このように条件式の値を利用したプログラムを書くことが出来ます。
ある意味では、条件式は関数のように見えることがありますが
関数とは異なります。
これは次のプログラムで確かめることが出来ます。
自作if関数との比較
> let myif cond e1 e2 = if cond then e1 else e2;; //condがtrueならe1,falseならe2の値を返す
val myif : bool -> 'a -> 'a -> 'a
> myif (1+1=2) "ok" "ng";; //ちゃんと動いてるように見える??
val it : string = "ok"
> if (1+1=2) then "ok" else "ng";;
val it : string = "ok"
> if true then printfn "ok" else printfn "ng";; //しかし、副作用のある式を書くと
ok
val it : unit = ()
> myif true (printfn "ok") (printfn "ng");; //結果が違う
ok
ng
val it : unit = ()
関数は引数を全て評価しますが、
条件式は全ての式を評価するとは限らないため
このような違いが出ます。
そのため、次のような例では
myifのほうは無限ループしてしまいます。
(注意:動かす際はCtrlキー+Cなどで止めるなどしてください)
無限ループしてしまうmyif
> if true then (printfn "ok") else while true do () done;;
ok
val it : unit = ()
> myif true (printfn "ok") (while true do () done);;
ok
- Interrupt
ここで出てきたwhileというのは、ループを記述するための構文です。
詳細はwhileについての章で解説しますが
while true do () doneで無限ループします。
条件式の構文
最後に、仕様書での条件式の構文を載せます(f#4.1 spec 6)
- conditional expression
-
if expr then expr elif-branchesopt else-branchopt
elif-branches := elif-branch ... elif-branch
elif-branch := elif expr then expr
else-branch := else expr
また、後に説明するパターンマッチを使えば
if expr1 then expr2 else expr3という式は
match (expr1:bool) with true -> expr2 | false -> expr3と等価で
if expr1 then expr2という式は
match (expr1:bool) with true -> expr2 | false -> ()と等価です(6.6.3)
本ページの旧版では、elifの説明が抜けており、
いげ太様にご助言頂きました。ご指摘感謝いたします。