日本語のパスを含むSubversionのリポジトリをMercurialのリポジトリに変換する

SubversionリポジトリMercurialリポジトリに変換するには
「hg convert」コマンドで簡単に変換できる。

しかし、リポジトリに日本語のパスを含む場合、日本語のパス対応がバージョン管理システムごとに異なるため問題が出る。

※各バージョン管理システムの日本語パスの対応

Subversionから変換した日本語パスの問題

  • 「hg update」した場合、Subversionから変換した日本語パスのファイルが文字化けしてしまう。
  • 日本語対応させるwin32mbcsのエクステンションを使用するのが常套手段であるが、win32mbcsを有効にした場合、Subversionから変換した日本語パスがあるとshift-jisに変換できないため「hg update」などのリポジトリの操作ができない。

普通はファイルを1つ1つ手作業で消去と追加を繰り返すことだろう。
でも、次のような方法をとれば一括で移行でき、なおかつ履歴もほとんど残る。

  1. 「hg convert」でリポジトリを変換
  2. win32mbcsを無効にした状態で、「hg update」でMercurialの作業ディレクトリを更新する。文字化けファイルが発生する。statusを確認すると、文字化けファイルは「?」マーク(管理外のファイル)になっており、リポジトリ上のファイルは「!」マーク(作業ディレクトリにいないファイル)になっている。
  3. Subversionの作業ディレクトリから最新版を取得し、Mercurialの作業ディレクトリにコピーする。文字化けファイルの位置に文字化けしていないファイルが配置される。
  4. 「hg addremove --similarity 100」でMercurialに履歴を追跡させる。文字化けファイルは削除され、代わりに削除されたファイルと同じ日本語パスの文字化けしていないshift-jis版が追加される。
  5. 「ソ」などのshift-jisのファイルがある場合、「hg addremove」が失敗する。その場合、失敗するファイルを「hg rm」で削除する。
  6. win32mbcsを有効にし、先ほど失敗した「ソ」などのshift-jisのファイルを追加する

それぞれのコマンドの詳細はマニュアルを参考にしてほしい。

追記

Pythonチュートリアルにしっかりと書いてありますね。
http://www.python.jp/doc/release/tut/node6.html#SECTION006710000000000000000
4.7.1 デフォルトの引数値

デフォルト値は、関数が定義された時点で、関数を 定義している 側のスコープ (scope) で評価されるので、

i = 5

def f(arg=i):
    print arg

i = 6
f()

は 5 を出力します。

C#でもやってみる

C#でも同じことをやってみました。

これではだめで、

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<int, int, int>[] lams = new Func<int, int, int>[10];

            foreach (var x in Enumerable.Range(0, 10))
            {
                lams[x] = (l, m) => l * x + m;            
            }
            Array.ForEach(lams, (lam) => Console.WriteLine(lam(1, 5)));
        }
    }
}

こっちでは、うまくいきます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<int, int, int>[] lams = new Func<int, int, int>[10];

            foreach (var x in Enumerable.Range(0, 10))
            {
                int y = x;
                lams[y] = (l, m) => l * y + m;
            }
            Array.ForEach(lams, (lam) => Console.WriteLine(lam(1, 5)));
        }
    }
}

以下、C#3.0ぽい書き方にしてみました。
メソッドチェインで。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var lams2 = Enumerable.Range(0, 10).Select(x => (Func<int, int, int>)((int l, int m) => l * x + m));

            foreach (var item in lams2)
            {
                Console.WriteLine(item(1,5));
            }
        }
    }
}

LINQで。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var lams2 = from x in Enumerable.Range(0, 10)
                        select (Func<int, int, int>)((int l, int m) => l * x + m);

            foreach (var item in lams2)
            {
                Console.WriteLine(item(1,5));
            }
        }
    }
}

Pythonのリスト内包のスコープ

http://d.hatena.ne.jp/EnogunoCap/20080703/1215056587
Pythonで遊んでたら疑問点が・・・」を見ていたら
C#で同じ話があったことを思い出しました。

http://www.atmarkit.co.jp/fdotnet/csharp20/csharp20_05/csharp20_05_02.html

上のリンク先で載ってました。

問題は、結局関数オブジェクト(lambda式)でループ変数を
そのまま利用していることなので、
解決策はリンク先と同じく一度別な変数に代入することで
うまくいきます。

でも、Pythonのlambda式は代入が使えません!!

代入に似た処理を・・・といろいろ試した結果
キーワード引数デフォルト引数が代入になることがわかりました!!

>>> lams = [ lambda l,m : l * x + m for x in range(10) ]
>>> [ lam( 1, 5 ) for lam in lams ]
[14, 14, 14, 14, 14, 14, 14, 14, 14, 14]

同じ数値が並ぶ結果となるところを、一度yに代入することによって

>>> lams = [ lambda l,m,y=x : l * y + m for x in range(10) ]
>>> [ lam( 1, 5 ) for lam in lams ]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

となります。

ちなみに、Python3.0b1ではxは[ ]内のみのスコープとなっているようで
xと打つとエラーとなります。

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>

微妙に仕様が変わっているようです。*1

*1:2.x系のソースを全自動で3.0仕様にコンバートするって言ってるけど大丈夫かなぁ

結局

意外と使えるかも。

特に「セーブするまでプロジェクトファイル群を作らない」設定にしておけば
テンポラリフォルダにプロジェクトが作られるため、
プログラムの実行後セーブしないで終了すればきれいさっぱり消せるし。

ただ、デバッグしにくいのでメソッドチェーンや引数内での評価を多用する必要はないかな。

レシピ087 複数の配列を並行処理する

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var aarr = new[] { 1, 2, 3 };
            var barr = new[] { "a", "b", "c" };
            var zarr = new object[3];

            for (int i = 0; i < aarr.Length; i++)
            {
                zarr[i] = new object[] { aarr[i], barr[i] };
            }

            Array.ForEach(zarr, x => {
                Array.ForEach((object[])x, y => Console.Write(y));
                Console.WriteLine();
            });
        }
    }
}

これは無理ですね。

レシピ083 配列を作成する

意外と健闘していると思う。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            レシピ083配列を作成する3_1_Array();
            レシピ083配列を作成する3_1_List();
            //レシピ083配列を作成する3_2はレシピ083配列を作成する3_3と同じ
            レシピ083配列を作成する3_3_Array();
            レシピ083配列を作成する3_3_List();
            レシピ083配列を作成する4_1_List();
            レシピ083配列を作成する4_2_ReadLine();
            レシピ083配列を作成する4_2_ReadToEnd();
            //レシピ083配列を作成する5はレシピ083配列を作成する4_2_ReadToEndに含む
        }

        private static void レシピ083配列を作成する3_1_Array()
        {
            var book = new[] { "title", "author", "publisher" };

            Array.ForEach(book, x => Console.WriteLine(x));
        }

        private static void レシピ083配列を作成する3_1_List()
        {
            var book = new List<string> { "title", "author", "publisher" };

            book.ForEach(x => Console.WriteLine(x));
        }

        private static void レシピ083配列を作成する3_3_Array()
        {
            var book = "book title:author:publisher".Split(':');

            Array.ForEach(book, x => Console.WriteLine(x));
        }

        private static void レシピ083配列を作成する3_3_List()
        {
            var book = "book title:author:publisher".Split(':').ToList();

            book.ForEach(x => Console.WriteLine(x));
        }

        private static void レシピ083配列を作成する4_1_List()
        {
            var records = new List<string>();
            records.Add("title");
            records.Add("author");
            records.Add("publisher");

            records.ForEach(x => Console.WriteLine(x));
        }

        private static void レシピ083配列を作成する4_2_ReadLine()
        {
            using (var file = new System.IO.StreamReader("data.txt"))
            {
                var buf = new List<string>();
                while (!file.EndOfStream)
                {
                    buf.Add(file.ReadLine());
                }

                buf.ForEach(x => Console.WriteLine(x));
            }
        }

        private static void レシピ083配列を作成する4_2_ReadToEnd()
        {
            using (var file = new System.IO.StreamReader("data.txt"))
            {
                var buf = file.ReadToEnd().Split().ToList();
                buf.RemoveAll(line => line.Length == 0);

                buf.ForEach(x => Console.WriteLine(x));
            }
        }

        private static void レシピ083配列を作成する4_2_没案()
        {
            using (var file = new System.IO.StreamReader("data.txt"))
            {
                var buf = new List<string>();
                var array = file.ReadToEnd().Split(new[] { '\r', '\n' });
                Array.ForEach(array, line => { if (line.Length > 0) buf.Add(line); });

                buf.ForEach(x => Console.WriteLine(x));
            }
        }
    }
}