2010年11月21日日曜日

Perlのsplit関数で詰んだ

まず結論から。split関数の分割数を無制限したいときはなるべく第3引数に-1を指定すること。
何故かと言うと、split関数は「要求される値の数」が不明のとき、返り値に含まれるはずの空文字列を「無」にしてしまうのである。

以下の例を見てほしい。
  local $, = ',';
  my($foo,$bar,$baz) = split '<>','<><>';
  print map { defined $_ } ($foo,$bar,$baz);
勿論結果は「1,1,1」だ。では、次の例ではどうだろうか。
  local $, = ',';
  my($foo,$bar,$baz) = map { $_ } split '<>','<><>';
  print map { defined $_ } ($foo,$bar,$baz);
結果は「,,」。つまり、split関数の戻り値が空の配列であるということ。
前者の例では、split関数に「$foo,$bar,$bazに戻り値が格納されますよ」というのが伝わっているが、後者の例では「戻り値は配列を所望します」ということしか伝わっていないのである。

最初に、「要求される値の数」が不明のとき、返り値に含まれるはずの空文字列を「無」にしてしまうと説明したが、
つまり無制限のリストコンテキスト(map関数の第二引数等)で評価するとsplit関数は空文字列を「無」にし、
  my($foo,$bar,$baz) = split '<>','<><>';
のような時は「3つの値が要求されている」ことになり、空文字列は空文字列のまま返す。

この問題の解決策が最初に書いたsplit関数の分割数を無制限したいときはなるべく第3引数に-1を指定することである。
  local $, = ',';
  my($foo,$bar,$baz) = map { $_ } split '<>','<><>',-1;
  print map { defined $_ } ($foo,$bar,$baz);
結果はちゃんと「1,1,1」。別に-1でなくとも、「要求する値の数」であっても問題ない(この例では3)。

今回の件でsplit関数の第3引数を省略すると単に無制限に分割するのではなく、「要求される値の数」を解釈し、無制限のリストコンテキストで評価された時には空文字列を省いてしまうということがわかった。

ウィキペディアンみたいに書いてしまったけど、非常に解り難いような。。。これで大丈夫かな?
舞い上がって書いた記事なので間違いがあるかも・・・各自色々試してみてください。

0 件のコメント:

コメントを投稿