演算子の優先順位と結合性

演算子の優先順位について既にご存じの方は こちらにまとめてあります 演算子の優先順位と結合性を理解すると パイプライン演算子などの動作が理解しやすくなるため このセクションに記載します。 F#では、式の計算を行う上で演算子が現れるときに どのような順序で各部分式を評価するかを決める要素として 演算子の優先順位と結合性があります。 基本的には優先順位が高いものから評価され 優先順位が同じ場合は結合性によって評価順序が決まります。 この結合性には2種類あり、 右側から評価するものを右結合 左側から評価するものを左結合と呼びます。 こんな感じになります。
右結合と左結合
1 X 2 X 3において、Xが演算子だとすると
((1 X 2) X 3) //Xが左結合だとこう評価される
(1 X (2 X 3)) //Xが右結合だとこう評価される
おおかたの演算子(関数適用も)は左結合となっており、 また、式を()でくくった場合は()の中が優先して評価されます。 まず、優先順位が異なる場合の例です。 足し算+(左結合)はかけ算*(左結合)よりも優先順位が低いため この式は
結合の強さ
1 + 2 * 3 +4 * 5 + 6;;
こう評価されます。
結合の強さ
1 + (2 * 3) + (4 * 5) + 6;;
もしも設計ミスで+と*の優先順位が同じでどちらも左結合だったならば
結合の強さ
(((((1 + 2) * 3) + 4) * 5) + 6);;
こう評価されてしまいます。 また、結合の強さが同じ場合は結合性で計算順序が決まります。 以下の例では、**(冪)が右結合であることが確認出来ます。
結合性
> 2.0 ** 3.0 ** 2.0;;
val it : float = 512.0
> 2.0 ** (3.0 ** 2.0);;
val it : float = 512.0
> (2.0 ** 3.0) ** 2.0;;
val it : float = 64.0
ただし、これは演算子が複数ある場合の話であって 右結合であっても 演算子が一つだけの場合は 普通に左側から評価されます。
テストコード
let lefter ()= printfn "left";2.0;;
let righter ()= printfn "right";3.0;;
(lefter ()) ** (righter ());;
また、関数適用の順序を理解するには 「F#の関数は全て1引数関数のように扱える」 ということを考慮にいれる必要があります。
関数適用
let h x = x * 2;;
let f x y z = x + y + z;;
//この3つは同じ意味
f 1 2 3;;
(f 1) 2 3;;
((f 1) 2) 3;;

//エラー (f 1) + ((2 3) 4)と判断される。
f 1+2 3 4;; //関数適用のほうが+よりも優先順位が高い
h 1 + 2;; //(h 1) + 2。 h (1 + 2)ではない
h(1 + 2);;
1 + h 2;; // 1 + (h 2)
2 3;; //エラー。ちょっと無理な例ですが、これは関数適用と見なされる
全ての関数が1引数というのは 連続して関数を適用する場合少し不便です。 例えばこんなケース
関数合成がしたいが
> let f x = x + 1;;
val f : int -> int
> let g x = x * 2;;
val g : int -> int
> f g 3;; //エラー
> f (g 3);; //これならOK
//例えばこんなのを書きたい場合は大変
> f (g (h (i (j (k x)))))
このようなケースのために パイプライン演算子がつかえます。
パイプライン演算子を利用した例
let f x = x + 1;;
let g x = x * 2;;
let h x y = x + y;;
3 |> g |> f |> f |> f;; //(f (f (f (g 3))))を計算
f <| f <| f <| g 3;;	//これはエラー。HubFSで議論あり
h <| f 2 <| g 3;;		//しかしこういう使い方が出来るという議論も