ソースコードの歩き方《IronPython》map 5/5

Previous|5/5|Next
Python.use(better, src=”IronPython”) # ソースコードを散策する記事一覧

無限ループから抜け出す鍵は

for ブロックの後には、次のコードの断片があります。

    while (true) {
bool done = true;
for (...) {
...
done = false; // 未処理の要素が残っている
}
if (done) { // すべての要素を処理した
return ret;
}
... // 次の要素を処理する
}

この無限ループから脱出する鍵が、変数 done です。未処理の要素が残っている間は、適切な処理を繰り返します。すべての要素を適切に処理し終えたら、そこでリターン値を返します。ここでも、まず全体を俯瞰するのが大切です。そして、無限ループから抜け出す鍵を、忘れずに探すことです。

シーケンスの長さが違う場合

複数のシーケンスが指定されると、その長さが違うことがあります。短いために、対応する要素がないときには、そこに None があるものと見なします。


    while (true) {
bool done = true;
for (int i = 0; i < enums.Length; i++) {
if (enums[i].MoveNext()) {
args[i] = enums[i].Current;
done = false; // 未処理の要素が残っている
} else {
args[i] = null;
}
}
... // 次の要素を処理する
}

ここにも、前述した IEnumerator のイディオムの変形版が見られます。
条件式 i < enums.Length が成立する間は、for ブロック内の処理を繰り返すので、すべてのシーケンス enums を一巡します。各シーケンス enums から要素をひとつずつ取り出して、それを作業用の args に入れておきます。このとき、鍵となる変数 done の値をリセット(false)するので、最も長いシーケンスの要素を処理するまで、無限ループから抜け出しません。ただし、次の要素が得られないなら、作業用の args に目印として null(後で None になる)を入れておきます。
すると、次のようなテストケースを思い付きます。

>>> map(lambda a,b: a+str(b), "ABC", "12")
['A1', 'B2', 'CNone']

最も長い文字列の要素 "C" には対応するものがないので、そこに None が入ります。


関数を省略した場合
第1引数 function に None が指定されたときには、関数を省略したものとみなされて、与えれた各シーケンスと同じ要素を持つタプルを生成します。

    while (true) {
bool done = true;
...
if (func != null) { // 次の要素を処理する
ret.AddNoLock(Ops.Call(func, args));
} else {
ret.AddNoLock(Tuple.MakeTuple(args));
args = new object[enums.Length];
}
}

関数が指定されたなら、条件式 func != null が成立するので、関数を配列 args の要素すべてに適用した結果 func(args) を、リスト ret に追加します。関数が指定されないなら、配列 args と同じ要素を持つタプルを生成して、それをリスト ret に追加します。そして、args には、新たな作業領域を確保します。
ここでは、配列 args からどのようにしてタプルが得られるか(how)は知らなくても、それを使ってなにをしたいか(what)を理解できれば、十分です。args と同じ要素を持つタプルが欲しいときに、Tuple.MakeTuple を利用するだけです。
すると、次のようなテストケースを思い付きます。

>>> map(lambda a,b: a+b, "ABC", "123")
['A1', 'B2', 'C3']

複数の文字列から1文字ずつ取り出して、それらを連結した文字列を生成します。

>>> map(None, "ABC", "123")
[('A', '1'), ('B', '2'), ('C', '3')]

関数を省略すると、連結した文字列を生成した後で、それをもとにタプルを生成します。ただし、タプルを生成したいなら、次のように、

>>> zip("ABC", "123")
[('A', '1'), ('B', '2'), ('C', '3')]

組み込み関数 zip を利用すると便利です。

>>> map(None, "ABC", "12")
[('A', '1'), ('B', '2'), ('C', None)]

最も長い文字列の要素 "C" には対応するものがないので、そこに None が入ります。
これで、その動作を理解するのに必要なコードを読破したことになります。□


Previous|5/5|Next