ソースコードの歩き方《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 があるものと見なします。
ここにも、前述した 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 が入ります。
これで、その動作を理解するのに必要なコードを読破したことになります。□