演算子
演算子(operator)とは、演算内容を指示する記号のことです。
演算対象の値のことをオペランド(operand)と呼び
単項演算子:オペランドが1つのもの。英語ではunary operator
二項演算子:オペランドが2つのもの。英語ではbinary operator
といった用語の使い方をします。
具体的には足し算引き算のプラスとかマイナスが2項演算子の例です。
マイナス10を-10と表記するときは、先頭の-が単項演算子になります。
演算子は、関数(後に説明)のように使えますが、
関数とは実行する際に計算される順番が違います。
(詳細は演算子の優先順位と付録で説明)
ここで説明する演算子は&&と||を除き
Microsoft.FSharp.Core.Operatorsで定義されています。
算術演算子/型の変換を行う演算子
基本的な算術演算子には以下のものがあります(17.2.1)
基本的な算術演算子(Basic Arithmetic Operators)
7 + 2;; //足し算
7 - 2;; //引き算
7 * 2;; //かけ算
7 / 2;; //割り算(商)
7 % 2;; //割り算(余)
-7;; //符号の反転
not true;; //論理値の否定。bool値のみ
-(符号の反転)とnot(論理値の否定)は単項演算子で
他は二項演算子です。
-には二通りの意味があることになります。
notだけは、bool型の値にしか使用できないのですが
他の演算子は全て、オーバーロード(overload)されているため
様々な型の値に対して使用できます。
オーバーロードというのは
同じ名前で、異なる種類の引数や返り値の型をサポートすることです。
(引数や返り値の意味については、基礎編のラムダ式で説明します)
つまり、同じ+という演算子記号が
次のように様々な型同士で利用出来ます。
+演算子のオーバーロード
"hello" + "world";; //文字列の連結
7uy + 2uy;; //byte型同士の足し算
7.0 + 2.0;; //float型同士の足し算
7.0f + 2.0f;; //float32型同士の足し算
7s + 2s;; //int16型同士の足し算
7n + 2n;; //nativeint型同士の足し算
ここで紹介した二項演算子は、基本的に
同じ型の数値型同士でしか使えないと覚えて問題無いと思います。
(オーバーロードのやり方によってはそうでない場合もあり得ます)
異なる数値型同士の加算はエラー
1.0 + 2;; //floatとintを足そうとした!
このような場合は、次のように
値の型を変換するための演算子(Overloaded Conversion Functions)(f#4.1 spec 18.2.10)を使います
この変換では
以下のものが使えます(17.2.11)
byte/sbyte/int16/uint16/int32/int/uint32/int64/uint64/
nativeint/unativeint/float/double/float32/single/decimal/char/enum
まだ例外などいくつかの機能について説明していないので
以下の部分は、今は飛ばしても問題ないです
整数の場合x%yはx - (x/y)*yで計算されます。
また、整数による0除算と0での剰余計算では
System.DivideByZeroExceptionが発生します。
(例外については別のページで説明します)
例外の発生
> 1/0;;
System.DivideByZeroException: 0 で除算しようとしました。
場所 <StartupCode$FSI_0082>.$FSI_0082.main@()
Stopped due to error
> 1%0;;
System.DivideByZeroException: 0 で除算しようとしました。
場所 <StartupCode$FSI_0083>.$FSI_0083.main@()
Stopped due to error
single,floatの場合、0.0で割っても例外は発生しないので注意してください。
floatでのゼロ除算
> 1.0 / 0.0;;
val it : float = infinity
> 1.0 / -0.0;;
val it : float = -infinity
> 1.0 % 0.0;;
val it : float = nan
/や%の動作の詳細は、F#はC#の仕様書に従っているので
これらの一番正確な情報源はC#の仕様書になります。
参考情報:+の型(f#4.1 spec 5.2.3)
+の型
val inline (+) : ^a -> ^b -> ^c
when (^a or ^b) : (static member (+) : ^a * ^b -> ^c)
Generic Equality and Comparison Operators
値の大小比較や、値が等しいかを調べるための演算子です(f#4.1 spec 18.2.2)
max,minはそれぞれ、2つの値を比べて大きい方、小さい方を返し
他の演算子は、2つの値を比べbool型の値を返します。
・true(条件が成り立つ時)
・false(条件が成り立たない時)
Generic Equality and Comparison Operators
1=2;; //1と2が等しいかどうか
1<>2;; //1と2が異なるかどうか
1<2;; //1が2より小さいかどうか
1>2;; //1が2より大きいかどうか
1<=2;; //1が2以下であるか
1>=2;; //1が2以上であるか
max 1 2;; //1と2のうち大きい方
min 1 2;; //1と2のうち小さい方
浮動小数点(1.5とか3.2fとか)を比較する場合は、
ビット単位で等しいかが比較されます
(f#4.1 spec 7.1 note / ecma335 8.2.5.1/8.2.5.2)
これらの演算子は賢く、様々な型を再帰的に比較を行います。
比較の詳細は(f#4.1 spec 8.15.3/8.15.4)に記載があります。
再帰的な比較
> (1,2)<(3,4);;
val it : bool = true
> (1,2)<(3,2);;
val it : bool = true
> "hello"<"hallo";;
val it : bool = false
> (1,[|[2;3];[4;5];|]) < (1,[|[2;3];[4;6]|]);;
val it : bool = true
> [(1,2);(3,4);] < [(1,1);(10,10)];;
val it : bool = false
上のサンプルコードでは
タプル、リスト、配列を使っています。
2つ以上の値を,で区切って()でかこんだものをタプル、
[]でかこんだものをリスト
[||]でかこんだものは配列というのですが、
これらについては、基礎編の各ページを参照してください。
この他、レコードや判別共用体(discriminated union)も賢い比較をしてくれますが
クラス等の.Net Frameworkでの参照型にあたるもの同士の比較は、
参照が等しいかが比較されるだけであるため
上のような、中身の値での比較が行いたい場合は
Object.Equalsメソッドをオーバーライド(後に解説)する必要があります。
詳細はオブジェクト指向編で説明予定です。
ブール演算子
bool型の値を扱うための演算子で、notと&&と||の3種類があり、
notは論理否定、&&は論理積、||は論理和に相当します。
notは単項演算子で、入力がtrueならfalse,入力がfalseならtrueを返します。
&&と||は2つの入力を持ちます。
&&は2つの入力がtrueの場合にtrue、それ以外はfalseを返します。
||は2つの入力がfalseの場合にfalse、それ以外はtrueを返します。
このサンプルは、全てのbool値の組み合わせでの
実行結果を表示しています。
ブール演算子
> not true;;
val it : bool = false
> not false;;
val it : bool = true
> true && true;;
val it : bool = true
> true && false;;
val it : bool = false
> false && true;;
val it : bool = false
> false && false;;
val it : bool = false
> true || true;;
val it : bool = true
> true || false;;
val it : bool = true
> false || true;;
val it : bool = true
> false || false;;
val it : bool = false
&&と||は他の演算子とは異なる特殊なルールを持っていて
expr1 && expr2は
if expr1 then expr2 else false
expr1 || expr2は
if expr1 then true else expr2
に変換されて実行されます(f#4.1 spec 6.5.4)
(ifは基礎編のifのページで説明していますのでそちらを参考にしてください)
そのため、
&&であればexpr1がfalseの場合
||であればexpr1がtureの場合
expr2は全く評価されません。
この性質を短絡評価、ショートサーキット評価(short-circuit evaluation)または
最小評価(minimal evaluation)と呼ぶことがあります。
仕様ではnotは算術演算子(f#4.1 spec 18.2.1)
&&と||はShortcut Operator Expressions(f#4.1 spec 6.5.4)に記載されていますが、
MSDNにならってブール演算子としてまとめています。
ビット処理演算子
-ここより下の演算子は、最初は飛ばしても問題ないです-
ビット処理を行うための演算子です(f#4.1 spec 18.2.3)
以下のいずれかの型に対して使えます(
参考)
byte, sbyte, int16, uint16, int32 (int),
uint32, int64, uint64, nativeint, and unativeint
ビット処理演算子(bitwise operators)
//10は二進数で1010
//12は二進数で1100
10 &&& 12;; //論理積、同じビットが残る。結果は二進数で1000
10 ||| 12;; //論理和、片方でも1なら残る。結果は二進数で1110
10 ^^^ 12;; //排他的論理和、違うビットだけ残る。結果は二進数で0110
~~~ 0b11111111111111111111111111110101;; //ビット反転。結果は二進数で1010
10 <<< 1;; //1ビット左シフト。結果は20。二進数で10100
10 >>> 1;; //1ビット右シフト。結果は5。二進数で101
シフトの右側のオペランドはint型で与えます。
MSDNによると
右シフトの最上位のビット(MSB:most significant bit)は
0が詰められると書いてありますが
実際の所、
符号あり整数型なら算術シフト(arithmetic shift)
符号なし整数型なら論理シフト(logical shift)
という動作をしているように見えます。
Math Operators
数値計算を扱う際に使用できる演算子です(f#4.1 spec 18.2.4)
全てオーバーロードされています。
Math Operators
let pi = 3.1415926535;; //円周率
2.0 ** 3.0;; //冪。2の3乗を計算して8.0
pown 2 3;; //整数の冪。2の3乗を計算して8
abs (-10);; //絶対値を返す。この値は10
acos (-1.0);; //cosの逆関数。この値はpi
asin 1.0;; //sinの逆関数。この値はpi/2.0
atan 1.0;; //tanの逆関数。この値はpi/4.0
atan2 1.0 1.0;; //tan(引数1/引数2)の逆関数。
ceil 1.5;; //天井関数。x以上の最小の整数に丸める。この値は2.0
ceil (-1.5);; //この値は-1.0
cos pi;; //cosine関数。この値は-1
exp 1.0;; //自然対数の冪。この値は2.718281828
floor 1.5;; //床関数。x以下の最大の整数に丸める。この値は1.0
floor (-1.5);; //この値は-2.0
log (exp 1.0);; //自然対数。この値は1.0
log10 (10.0**2.0);; //常用対数。この値は2.0
round 0.5;; //入力値にもっとも近い整数に丸める。結果は0.0
round 1.5;; //入力値にもっとも近い整数に丸める。結果は2.0
sign 10;; //符号を返す。この値は1
sign 0;; //この値は0
sing (-3);; //この値は-1
sin (pi/2.0);; //sine関数。この値は1.0
sqrt 4.0;; //平方根。この値は2.0
tan (pi/4.0);; //tangent関数。この値は1.0
//この他、双曲線関数を計算するsinh,cosh,tanhもあります
roundは単に入力値に「最も近い整数にまとめる」とだけ
MSDNには記述があるのですが
どちらにも丸められる場合は最も近い偶数に丸められるように見えます。
パイプライン演算子、合成演算子(Function Pipelining and Composition Operators)
関数の適用順序を制御したり、関数を合成するのに使う演算子です(f#4.1 spec 18.2.5)
パイプライン演算子
let sub a b = a - b
let sub10 x = x - 10;;
10 |> sub10;; //パイプライン演算子|>。結果は0
10 |> sub10 |> sub10;; //-10。((10 |> sub10) |> sub10)と評価
sub10 20;; //通常の関数適用。結果は10
sub10 <| 20;; //パイプライン演算子<|。結果は10
sub 10 20;; //通常の関数適用。結果は-10
sub 10 <| 20;; //パイプライン演算子<|。結果は-10
sub <| 10 <| 20 //パイプライン演算子<|。結果は-10
//sub10 <| sub10 <| 20;; //エラー。((sub10 <| sub10) <| 20)と評価
sub10 <| (sub10 <| 20);; //0
パイプライン演算子はどちらも左結合です。
そのため、<|はhaskellの$(右結合)とは異なります。
これについては、別途説明予定です。
関数合成
let add1 x = x + 1;; //1足す関数
int twice x = x * 2;; //2倍にする関数
let add1twice = add1 >> twice;; //関数合成。1足して二倍にする関数
let twiceadd1 = add1 << twice;; //関数合成。二倍して1足す関数
add1twice 10;; //結果は22
twiceadd1 10;; //結果は21
f >> gはg(f(x))、
f << gはf(g(x))と実行するように関数が合成されます。
オブジェクト変換演算子(Object Transformation Operators)
様々な変換を行う演算子です(f#4.1 spec 18.2.6)
box/unbox/sizeof/hash/typeof/typedefof/ref/!の8種類があり
box/unboxについてはオブジェクト指向偏のobj型
ref/!については基礎編の参照型を参照してください
Object Transformation Operators
sizeof<int>;; //int型の内部サイズを返す
sizeof<string>;; //string型の内部サイズを返す
hash "str";; //ハッシュ値を計算する
hash ("a",1);; //色んな型で使える
typeof<int>;; //与えられた型のSystem.Type表現を返す。色々な情報がわかります。
typedefof<int>;; //typeofに加え、generic typeの場合はGetGenericTypeDefinitionを呼ぶ
Pair Operators
2要素のタプルから要素を取り出す演算子で
fstとsndの二種類があります(f#4.1 spec 18.2.7)
3要素以上を扱う場合はパターンマッチを使います(基礎編のタプルを参照ください)
Pair Operators
> fst (1,2);; //最初の要素を取得
val it : int = 1
> snd(1,2);; //二番目の要素を取得
val it : int = 2
Exception Operators
例外を扱うための演算子です(f#4.1 spec 18.2.8)
failwith/invalidArg/raise/reraiseの4種類があります。
これは基礎編の例外で扱う予定です。