Jun 6, 2010

C# : Flatten()のようなもの

RubyでいうArray#flattenに相当するメソッドがC#には無いということで、似たものを書いてみたのがこちら。System.Arrayに対する拡張メソッドとなっています。

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4.  
  5. namespace FlattenTest
  6. {
  7.     public static class ExtensionMethods
  8.     {
  9.         /// Ref.: Flatten Ruby method in C# - Stack Overflow
  10.         ///   http://stackoverflow.com/questions/197081/flatten-ruby-method-in-c
  11.         private static IEnumerable FlattenAux(this IEnumerable array)
  12.         {
  13.             foreach (var item in array)
  14.             {
  15.                 if (item is IEnumerable)
  16.                 {
  17.                     foreach (var subitem in FlattenAux((IEnumerable)item))
  18.                     {
  19.                         yield return subitem;
  20.                     }
  21.                 }
  22.                 else
  23.                 {
  24.                     yield return item;
  25.                 }
  26.             }
  27.         }
  28.  
  29.         /// Returns a one-dimensinal flattening of given array.
  30.         public static System.Array Flatten(this System.Array array)
  31.         {
  32.             var al = new ArrayList(array.Length);
  33.             foreach (var v in FlattenAux(array))
  34.                 al.Add(v);
  35.             return al.ToArray(al[0].GetType());
  36.         }
  37.     }
  38.  
  39.     class Program
  40.     {
  41.         static void Main(string[] args)
  42.         {
  43.             float[, ,] p0 = new float[3,2,2];
  44.             float[][][] p1 = new float[3][][] {
  45.                 new float[2][] { new float[2], new float[2] },
  46.                 new float[2][] { new float[2], new float[2] },
  47.                 new float[2][] { new float[2], new float[2] }
  48.             };
  49.             for (int i = 0; i < 3; ++i)
  50.                 for (int j = 0; j < 2; ++j)
  51.                     for (int k = 0; k < 2; ++k)
  52.                         p0[i, j, k] = p1[i][j][k] = 100 * i + 10 * j + k;
  53.  
  54.             float[] fp0 = (float[])p0.Flatten();
  55.             for (int i = 0; i < fp0.Length; ++i)
  56.                 Console.Write("{0} ", fp0[i]);
  57.             Console.WriteLine();
  58.  
  59.             float[] fp1 = (float[])p1.Flatten();
  60.             for (int i = 0; i < fp1.Length; ++i)
  61.                 Console.Write("{0} ", fp1[i]);
  62.             Console.WriteLine();
  63.         }
  64.     }
  65. }

May 16, 2010

乱数生成アルゴリズム WELL

Mersenne Twister (MT) の作者らによる比較的新しいアルゴリズム・ WELL (Well Equidistributed Long-period Linear) についてのメモです。
  • 原著はこちら:L'Ecuyer, Pierre; Panneton, François; Matsumoto, Makoto (2006), Improved Long-Period Generators Based on Linear Recurrences Modulo 2
  • 論文の訂正表はこちら。シフト演算の部分に関する誤りが訂正されているので必ず参考に。
  • Game Programming Gems 7 で紹介されています。記事を書いた Chris Lomont 氏 (http://www.lomont.org/) のサイトで原文が公開されています:
  • アルゴリズムの特徴は:
    • MTより計算が少しだけ重い。
    • MTは初期状態のビットパターンに0を多く含んでいると、出力する乱数のビットパターンにもしばらく0が多く含まれてしまう傾向がある。WELLはこのような状態からの脱出がMTに比べて早い。
  • C# (VS 2010) で周期 2^1024 バージョンの WELL を書いてみました:
  • 10000回の Monobit テストに対して不合格となるのは1〜2回程度。優秀だと思います。
    • System.Random を使ったら Monobit テスト 10000回に対して3000〜4000回しか合格しないケースがあることにびっくりです。

Apr 30, 2010

Vista : シンボリック リンクでフォルダを別ドライブへ移設

Windows Vista 以降からは mklink コマンドで「シンボリックリンク」を作成することができる。
これによりファイル/ディレクトリの実体をドライブをまたいで配置することが可能となっている。

この機能を利用して C:\Program Files\Adobe を D:\Program Files\Adobe へ移設する方法は次のようになる:

1. 管理者権限でコマンドプロンプトを起動
   (※「すべてのプログラム>アクセサリ>コマンド プロンプト」を右クリックし「管理者として実行」)

2. コマンドラインでの作業:
  C:\Windows\system32> cd "c:\Program Files"
  C:\Program Files> xcopy /E /H /I /K /X Adobe "D:\Program Files\Adobe"
  ...
  C:\Program Files> ren Adobe Adobe___
  C:\Program Files> mklink /D Adobe "D:\Program Files\Adobe"
  Adobe <<===>> D:\Program Files\Adobe のシンボリック リンクが作成されました
 
  C:\Program Files> rmdir /S Adobe___
(2013-01-22 追記)
Windows Vista/7 ならば Robocopy が標準で使えるので下記のほうがいいかも。
"C:\ProgramData\Microsoft"にある "Windows Defender" を D:\ProgramData\Microsoft に移すものとして:

  • 「Win+R(ファイル名を指定して実行)」で "cmd /f:on [Ctrl+Shift+Enter, 管理者権限で実行]"
  • コマンドラインから下記の順に実行
C:\ProgramData\Microsoft>robocopy "Windows Defender" "D:\ProgramData\Microsoft\Windows Defender" *.* /copyall /dcopy:t /mir
C:\ProgramData\Microsoft>ren "Windows Defender" "___Windows Defender"
C:\ProgramData\Microsoft>mklink /D "Windows Defender" "D:\ProgramData\Microsoft\Windows Defender"
C:\ProgramData\Microsoft>rmdir /S "___Windows Defender"

Apr 23, 2010

LINQ to XML メモ







  1. <version="1.0" encoding="utf-8"?>
  2. <game_consoles>
  3.   <ip_address_map>
  4.      <name="Xbox 360">192.168.11.2>
  5.      <name="PlayStation 3">192.168.11.3>
  6.      <name="Wii">192.168.11.4>
  7.      <name="3DO Real">192.168.11.5>
  8.      <name="DreamCast">192.168.11.6>
  9.   </ip_address_map>
  10. <game_consoles>
というXMLファイル・config.xml があるとき、「targetタグ」から「nameに3を含む」項目に次のようにしてアクセスできる:
  1. using System;
  2. using System.Linq;
  3. using System.Xml.Linq;
  4. // ...
  5.    XDocument config_xml = XDocument.Load(@".\config.xml");


  6.    var query = from c in config_xml.Descendants("target")
  7.                 where c.Attribute("name").Value.Contains("3")
  8.                 select c.Attribute("name").Value + " " + c.Value;
  9.     foreach (var q in query)
  10.     {
  11.         Console.WriteLine("{0}", q);
  12.     }


from/where/selectのあたりが LINQ (Language INtegrated Query) to XML と呼ばる仕組みを使っている部分。C# 3.0 (.NET Framework 3.5 以降) で利用可能。

ハッシュテーブルなど、別なデータ構造に入れる場合の例はこんな感じでいける:

 
  1. var platform_map = new Dictionary<string, string>();
  2. XDocument config_xml = XDocument.Load(@".\config.xml");
  3. var query = from c in config_xml.Descendants("target")
  4.                  where c.Attribute("name").Value.Contains("3")
  5.                  select new KeyValuePair<string, string>(c.Attribute("name").Value, c.Value);
  6.      foreach (var q in query)
  7.      {
  8.          platform_map[q.Key] = q.Value;
  9.      }
最初に登場した config.xml は、System.Xml.Linq 以下の XDocument などを使い次のようにして作成されたもの:





  1.     XDocument doc = new XDocument(
  2.         new XElement("game_consoles",
  3.            new XElement("ip_address_map",
  4.                 new XElement("target", new XAttribute("name", "Xbox 360"), "192.168.11.2"),
  5.                 new XElement("target", new XAttribute("name", "PlayStation 3"), "192.168.11.3"),
  6.                 new XElement("target", new XAttribute("name", "Wii"), "192.168.11.4"),
  7.                 new XElement("target", new XAttribute("name", "3DO Real"), "192.168.11.5"),
  8.                 new XElement("target", new XAttribute("name", "DreamCast"), "192.168.11.6")
  9.                 )
  10.             )
  11.         );
  12.     doc.Save(@".\config.xml");
XmlReader/XmlWriterを利用したコードに比べると格段に楽ですね。