Monoの奇妙な現象
ご存知の通り、大抵のプログラミング言語では「\t」がTabを挿入するエスケープシーケンスになっていると思いますが、C#でもそうです。「\t」じゃなくて「\t」です。
昨日課題の為に書いたC#のコードで、結果の出力時に「\t」を使って結果を整えて出力させようとしたのですが、Mac OS Xでは「\t」が無視されてしまいました。
CellularAutomataのコード
ルール184(184規則)に基づいたCellular Automata の空間時間図(時間経過とともに、どのように変化しているか)を出力するコードです。
using System; using System.Collections.Generic; public class CA { public static bool GetNext( bool prep, bool current, bool next ) { if ( prep ) // 1** { if ( current ) // 11* { if ( next ) return true; // 111 else return false; // 110 } else return true; // 10* } else // 0** { if ( current ) // 01* { if ( next ) return true; // 011 else return false; //010 } else return false; // 00* } } public static List<Boolean> GetRecord( List<Boolean> list ) { List<Boolean> tmp = new List<Boolean>(); for ( int i = 0; i < list.Count; i++ ) { if ( i == 0 ) tmp.Add( GetNext( list[list.Count - 1], list[0], list[1] ) ); else if ( i == list.Count - 1 ) tmp.Add( GetNext( list[i - 1], list[i], list[0] ) ); else tmp.Add( GetNext( list[i - 1], list[i], list[i + 1] ) ); } return tmp; } public static void Main( string[] args ) { string val = Console.ReadLine(); List<Boolean> list = new List<Boolean>(); CharEnumerator cenum = val.GetEnumerator(); cenum.MoveNext(); for ( int i = 0; i < val.Length; ++i ) { if ( cenum.Current.Equals( '0' ) ) list.Add( false ); else if ( cenum.Current.Equals( '1' ) ) list.Add( true ); cenum.MoveNext(); } Console.Write( "t = 0:\t" ); foreach ( var rel in list ) { if ( rel == true ) Console.Write( 1 ); else Console.Write( 0 ); } Console.WriteLine(); for ( int i = 0; i < 20; i++ ) { var result = GetRecord( list ); Console.Write( "t = {0}:\t", i + 1 ); foreach ( var rel in result ) { if ( rel == true ) Console.Write( 1 ); else Console.Write( 0 ); } Console.WriteLine(); list = result; } Console.ReadKey(); } }
実行結果
これを実行すると、任意の「0」と「1」の数字列を受け取ってルール184に則って変化させます。以下はWindowsでのVisual Studioにおける実行例。
1100010111011111110011010101011011111111 t = 0: 1100010111011111110011010101011011111111 t = 1: 1010001110111111101010101010110111111111 t = 2: 0101001101111111010101010101101111111111 t = 3: 1010101011111110101010101011011111111110 t = 4: 0101010111111101010101010110111111111101 t = 5: 1010101111111010101010101101111111111010 t = 6: 0101011111110101010101011011111111110101 t = 7: 1010111111101010101010110111111111101010 t = 8: 0101111111010101010101101111111111010101 t = 9: 1011111110101010101011011111111110101010 t = 10: 0111111101010101010110111111111101010101 t = 11: 1111111010101010101101111111111010101010 t = 12: 1111110101010101011011111111110101010101 t = 13: 1111101010101010110111111111101010101011 t = 14: 1111010101010101101111111111010101010111 t = 15: 1110101010101011011111111110101010101111 t = 16: 1101010101010110111111111101010101011111 t = 17: 1010101010101101111111111010101010111111 t = 18: 0101010101011011111111110101010101111111 t = 19: 1010101010110111111111101010101011111110 t = 20: 0101010101101111111111010101010111111101
今回注目してほしいのは、「t = n: *****」の所です。nが2桁以上の数字になっても***がずれていませんが、これは出力に「\t」を含ませたからです。が、これをMac OS X上のMonoで実行すると、「\t」が無視されて出力されちゃいます。
inohiro-mac:02-081212 inohiro$ gmcs CellularAutomata.cs; mono CellularAutomata.exe 011100110101110101110101111111 t = 0:10001001111011 t = 0:011100110101110101110101111111 t = 0:111010101011101011101011111110 t = 1:110101010111010111010111111101 t = 2:101010101110101110101111111011 t = 3:010101011101011101011111110111
MonoDevelopで「Unix互換の設定」とかそんなのがあったから、それを利用しないといけないのかな(コンパイルオプションみたいな?)と思ったのですが、そうでもない。Monoで実行するC#ではエスケープシーケンスが違うのかなとも考えたんですが、これも違いそう(それじゃダメでしょ)。
ググってみたところ、ImformIT:Introducing C# > Escaping Charactersにいくつかのエスケープシーケンスの利用方法(単純に記述の仕方ですが)が書かれていて、Monoコンパイラでコンパイルした後、実行した結果が載っています。やはり「\t」はTabを挿入するエスケープシーケンスとしては有効なようです。
記事のコード
using System; class Hello { public static void Main() { Console.WriteLine("Single quote: \'"); Console.WriteLine("Quotation mark: \""); Console.WriteLine("Backslash: \\"); Console.WriteLine("Alert: \a "); Console.WriteLine("Backspace: -\b"); Console.WriteLine("Formfeed: \f"); Console.WriteLine("Newline: \n"); Console.WriteLine("Carriage Return: \r"); Console.WriteLine("Tabulator: before\tafter"); Console.WriteLine("Tab: \v"); Console.WriteLine("binary 0: \0"); } }
私の環境での実行結果
inohiro-mac:02-081212 inohiro$ mcs hello.cs; mono hello.exe Single quote: ' Quotation mark: " Backslash: \ Alert: Backspace: - Formfeed: Newline: Carriage Return: Tabulator: before after Tab: binary 0:
ということで、「\t」でTabが入力できました。なぜ私が課題の為に書いたコードだと、Tabが挿入できないのでしょうか。
一つ考えたのはMonoコンパイラの使い方(?)でした。Monoコンパイラは「mcs」コマンドを用いて利用しますが、これは.NET 1.1をターゲットとしたコンパイラなので、「System.Collections.Generic」名前空間などの.NET 2.0以降の機能をusingディレクティブに追加していたり、ソースコード中で使っていると、
133051080127:02-081212 inohiro$ mcs CellularAutomata.cs CellularAutomata.cs(32,43): error CS1644: Feature `generics' is not available in Mono mcs compiler. Consider using Mono gmcs compiler instead
といったようなエラーが出ます。.NET 2.0をターゲットとしたコンパイラを用いるときは「gmcs」を使います。今回書いたコードでは「List
ということでコンパイラ説はすぐに無くなりました。
この謎は未だに(2008/12/19 00:34)解決していません。先ほど友達と晩ご飯を食べているときに、話したところ「文字コード」が原因ではないかといわれました。でもMacでもWindowsでもUTF-8で書いているはずなので、、、とよく考えるとTerminal.appがEUC-JPだったかもしれないので確認してきます(しかし文字コードが原因なのか...)。
Terminal.appはUTF-8でした。ということでUTF-8で書いてるはずなんですが、あれか、改行コードか。ううむ。
どこかに原因が分かる方はいらっしゃらないかな。
追記
Windows(Visual Studio)側でコンパイルしたバイナリ(「\t」がちゃんと使える)をMac OS X側に持って来て、Monoで実行したらどうかなと思って、やってみた。
inohiro-mac:Desktop inohiro$ mono CellulaAutomata.exe 01001011101101111001011101001111110 t = 0:01001011101101111001011101001111110 t = 1:00100111011011110100111010101111101 t = 2:10010110110111101010110101011111010 t = 3:01001101101111010101101010111110101
Tabは挿入されませんでした。ということで、これはコンパイラの問題ではなくて、ターミナルにおける「Tab」を表すエスケープシーケンスの違い(のようなもの?)が原因なのかもしれない(「\t」は挿入されてるけど、Terminal.appではそれがTabを表すエスケープシーケンスではない、とか)。
LinuxにMonoを入れて、検証してみるか。あとはiTermとか使ってみるとか。そもそもbashじゃダメなのか。ううむ。