Perl で `Use of uninitialized value concatenation` を出さないイディオムはありませんか?
Perl で文字列を生成するとき、よく Use of uninitialized value concatenation
を出してしまうので、どうにか解消したいと考えています。
これは初期化されていない(というか、undef が入っている)変数を文字列として結合したときに出ますが、毎回必ず初期値を入れるのはコード量が多くなるので嬉しくありません。
undef の場合に、空白になるようなイディオムはないのでしょうか?
例えば Ruby だと、変数が nil のとき、+
で文字列結合しようとするとエラーになりますが、
式展開するとエラーになりません。
( nil.to_s => ''
になるので )
irb(main):005:0> a = nil
=> nil
irb(main):006:0> a + "bbb"
NoMethodError: undefined method `+' for nil:NilClass
from (irb):6
from /usr/bin/irb:11:in `<main>'
irb(main):007:0> "#{a}bbb"
=> "bbb"
よって、Ruby では「文字列を生成するときは、必ず式展開を使う」というルールにしてしまえば、この系統のエラーは防げます。
一方、Perl では、文字列結合でも、変数展開でも、エラーはでませんが、どちらでも warning が出ます。
24> $b = undef
$res[18] = undef
25> $b . "222"
Use of uninitialized value $b in concatenation (.) or string at reply input line 1.
$res[19] = '222'
31> "${b}222"
Use of uninitialized value $b in concatenation (.) or string at reply input line 1.
$res[22] = '222'
この warning を出さないようにするには、どう書くのが良いでしょうか。
追記 2016-10-18
提示した例が、意図を説明できない例だったので、追記します。
(変数を宣言するときに初期値を入れるのは、当然ですよね……)
(id と 名前) のハッシュと、(id と 年齢) のハッシュ、があるとき、それを合成して表示することを考えます。
但し、年齢があるとは限りません。
21> my $ids_names = { 10 => '佐藤一郎', 20 => '鈴木二郎', 30 => '田中三郎' }
$res[15] = {
'10' => '佐藤一郎',
'20' => '鈴木二郎',
'30' => '田中三郎'
}
22> my $ids_ages = { 10 => 40, 30 => 20 };
$res[16] = {
'10' => 40,
'30' => 20
}
32> map { $ids_names->{$_} . " (" . $ids_ages->{$_} . ") さん" } keys %$ids_names;
Use of uninitialized value in concatenation (.) or string at reply input line 1.
$res[21] = [
'鈴木二郎 () さん',
'田中三郎 (20) さん',
'佐藤一郎 (40) さん'
]
このとき、Use of uninitialized value in concatenation
を避けるイディオムは、ないでしょうか?
ruby は、エラーになりませんし、
irb(main):001:0> ids_names = { 10 => '佐藤一郎', 20 => '鈴木二郎', 30 => '田中三郎' }
=> {10=>"佐藤一郎", 20=>"鈴木二郎", 30=>"田中三郎"}
irb(main):002:0> ids_ages = { 10 => 40, 30 => 20 }
=> {10=>40, 30=>20}
irb(main):008:0> ids_names.map { |id, name| "#{name} (#{ids_ages[id]}) さん" }
=> ["佐藤一郎 (40) さん", "鈴木二郎 () さん", "田中三郎 (20) さん"]
python は get で初期値が使えます。
In [1]: ids_names = {10: '佐藤一郎', 20: '鈴木二郎', 30: '田中三郎' }
In [2]: ids_ages = { 10: 40, 30: 20 }
In [7]: ["{} ({}) さん".format(name, ids_ages.get(id, '')) for (id, name) in ids_names.items()]
Out[7]: ['佐藤一郎 (40) さん', '鈴木二郎 () さん', '田中三郎 (20) さん']
perl も python に習って、
32> map { $ids_names->{$_} . " (" . ($ids_ages->{$_} // '') . ") さん" } keys %$ids_names;
のように書くのか、もっと良いイディオムがあるのか、知りたいと考えています。
(それなりに頻出だと思うのですが……)