if が文とは限らない話と,条件分岐で参照を返す話
(略)
でも、「式」しか書けない所ではそうはいかない。
例えば、(C++ で) 初期化リストの中や TMP では if 文なんて使えないので条件演算子を使わなければならない。
// ちと作為的な例だけど class hoge { const piyo& ref_p; public: hoge(int a, int b, const piyo& p1, const piyo& p2) : ref_p(a < b ? p1 : p2) {} };これは if 文では絶対に書けない。
おまけ:
C++ では、条件演算子を含む式を左辺に持ってくることも可能なのは覚えておいても損はない。
でも使うなよ?絶対使うなよ?
(a < b ? a : b) = c;
なんか話が混ざってる感があるので,もう少し成分分解してみる試み.
- if が常に文になってしまう言語の話
- 条件分岐の結果として,参照を返しうるかという話
if が常に文になってしまう言語の話
if というキーワードを使うとかならず文になってしまう言語があって,しかもそいつらは目立つという現実が.具体的には C/C++ とか Java とか C# とか Visual Basic 8 までとか.
が,シンタックスの議論は怖いので深入りしないことにして,この話はパス.
// F# let f c = if 'A' <= c && c <= 'Z' then (int)c - (int)'A' elif 'a' <= c && c <= 'z' then (int)c - (int)'a' + 26 elif '0' <= c && c <= '9' then (int)c - (int)'0' + 52 elif c = '+' then 62 elif c = '/' then 63 else -1 let g c = match c with | c_ when 'A' <= c_ && c_ <= 'Z' -> (int)c_ - (int)'A' | c_ when 'a' <= c_ && c_ <= 'z' -> (int)c_ - (int)'a' + 26 | c_ when '0' <= c_ && c_ <= '9' -> (int)c_ - (int)'0' + 52 | '+' -> 62 | '/' -> 63 | _ -> -1
条件分岐の結果として,参照を返しうるかという話
C よりも C++ を先に始めた自分のような人間には,
// C++ int a, b, c; ... (a < b ? a : b) = c; // (0) int& x = (a < b ? a : b); // (1) x = c; // (1')
の (0) は,単に (1) と (1') を組み合わせただけの話に見えます.(1) が可能なら (0) というシンタックスが成り立ってもまあいいじゃんという感じ.
んで,.NET 系の言語では,(1) が出来る言語が結構少なかったりします.Visual Studio 2010 に入っている言語だと,C++/CLI 以外では無理なんじゃないでしょうか.
// C++/CLI int a = 0, b = 1, c = 2; int% x = (a < b ? a : b); x = c;
C++/CLI と C# のこの違いは,値型インスタンスに対するメソッド呼び出しとして表面化させることができます.
// C# using System; struct Data { public int Id; public void SetId(int id) { Id = id; } } static class Program { static void Main(string[] args) { var cond = DateTime.Now.Ticks % 2 == 0; var data1 = new Data(); var data2 = new Data(); (cond ? data1 : data2).SetId(1); Console.WriteLine("{0} {1}", data1.Id, data2.Id); } } // 結果: 常に "0 0"
C++ や C++/CLI のシンタックスになじんだ人にとって,シンタックスが似ているのに結果が違うこの C# のコードはなんとも気持ち悪いはず.手元の環境で実験してみたところ,Visual Basic 9 で導入された if 演算子 も同じくコピーを返してました.
一方 Visual Studio 2010 beta1 に付属の F# は結構惜しい感じでした.F# は,byref 型をある程度扱えるみたいで,例えば以下のようなコードは OK です.
// F# let mymain = let mutable a = 0 let c = 2 let x = &a x <- c printfn "%d" a ()
ただ残念なことに,if 式の返値に byref 型を突っ込めないようです.次のコードは,コメントに書いた箇所でエラーになります.
// F# let mymain = let mutable a = 0 let mutable b = 1 let c = 2 // error FS0191: // A type instantiation involves a byref type. This is not permitted by the .NET runtime let x = if a < b then &a else &b x <- c printfn "%d %d" a b ()
気になるのでちょっくらフォーラムあたりで質問してみますかねぇ.