一日ズレてしまったが、Perlのエラーメッセージのコーナー。
2週間に一度、水曜日は、Perlのエラーメッセージが表示される最小のプログラムを書いて、どういうときにどう怒られるかを研究する。
そのはずであったが、最近はCPANモジュールのインストールの方法を書いていて、しばらくご無沙汰になってしまった。
今回は
%s argument is not a HASH or ARRAY element or slice
を研究する。

これは、

(主語S):%s argument
(動詞V):is
(補語C):not a HASH or ARRAY element or slice

という第2文型の文で、

(主語S):%s の引数
(動詞V):は
(補語C):HASHやARRAYの要素/スライスではない

という意味になる。
%sの引数はHASHやARRAYの要素/スライスではない
という訳になる。

これは、メッセージだけ眺めてみても意味が分からず、実際にプログラムで事故を起こすか、ラクダ本を眺めなければ分からない。
ハッシュや配列の要素、スライス以外のものにdelete関数を作用させたときに起こるメッセージである。

まず、エラーメッセージが発生しないプログラムを書く。
#! /usr/bin/perl
#
# deleteExp.pl -- delete関数の実験

use 5.010;
use strict;
use warnings;

my @arr = qw(foo bar baz qux);

say "1: array: @arr";

delete $arr[0]; #配列の要素をdelete

say "2: array: @arr";

delete @arr[1..2]; #配列のスライスをdelete

say "3: array: @arr";

delete $arr[4]; #配列の末尾の要素をdelete

say "4: array: @arr";

my %hash = (
 January => 1,
 February => 2,
 March => 3,
 April => 4,
 May => 5,
);

say "5: hash keys:", join(" ", sort keys %hash);

delete $hash{March}; #ハッシュの要素を削除

say "6: hash keys:", join(" ", sort keys %hash);

delete @hash{"January", "April"}; #ハッシュスライスを削除

say "7: hash keys:", join(" ", sort keys %hash);
上ではdelete関数を4回使って、配列の要素、配列のスライス、ハッシュの要素、ハッシュのスライスを削除して見ている。
結果はこうなる。
$ deleteExp.pl
1: array: foo bar baz qux quux number:5
Use of uninitialized value in join or string at /Users/query1000/perl/deleteExp.pl line 15.
2: array: bar baz qux quux number:5
Use of uninitialized value in join or string at /Users/query1000/perl/deleteExp.pl line 19.
Use of uninitialized value in join or string at /Users/query1000/perl/deleteExp.pl line 19.
Use of uninitialized value in join or string at /Users/query1000/perl/deleteExp.pl line 19.
3: array: qux quux number:5
Use of uninitialized value in join or string at /Users/query1000/perl/deleteExp.pl line 23.
Use of uninitialized value in join or string at /Users/query1000/perl/deleteExp.pl line 23.
Use of uninitialized value in join or string at /Users/query1000/perl/deleteExp.pl line 23.
4: array: qux number:4
5: array: number:0
6: hash keys:April February January March May
7: hash keys:April February January May
8: hash keys:February May
$
1:と2:の間で配列要素$arr[0]を削除している。
配列要素のdeleteはPerl 5.6からの新機能で、要素を削除すると言っても値を未初期化にするだけで、配列要素の位置はズラされない。
よって2:のsayの前で、未定義の$arr[0]を表示しようとして「Use of uninitialized value...」という警告が出ている。
また2:のsayの中では、barの前に$arr[0]があった場所にヌル文字列が空白に挟まれて表示されている。
配列の要素数も5のままである。

2:と3:の間で配列スライス@arr[1..2]を削除している。
これでbarとbazが削除され、$arr[3]であるquxのみがヌル文字列3個の後に表示される。
削除した配列要素を表示しようとして警告が3つ出ている。
配列の要素数は5のままである。

3:と4:の間で配列要素$arr[4]を削除している。
quuxが削除されたわけだが、警告は3つしか表示されていない。
これは、delete関数を配列要素に適用したとき、末尾以外の要素の場合は要素が未初期化されるだけだが(配列スロットは残り、後続の要素の位置はズレないが)、末尾の要素に適用したときは、配列の長さが縮むという現象である。
要素数が4に縮んでいる。

4:と5:の間で現状の末尾要素である配列要素$arr[3]を削除している。
全部の要素が削除されたのだが、警告は表示されず、要素数はゼロになっている。

Perlの配列は勝手に伸び縮みする。
$arr[100] = "hey!";
という式を書くと、$arr[0]から$arr[100]まで101個のスロットができ、101個目の配列要素$arr[100]に文字列「hey!」が入り、$arr[0]から$arr[99]まで未初期化の100個のスロットが出来る。
ここで戯れに
delete $arr[99];
とやってみても、もともと未初期化の$arr[99]が未初期化状態にされるだけなので、何も起きない。
しかし、
delete $arr[100];
とすると、唯一の初期化された配列要素である$arr[100]が削除されるので配列そのものが抹消される。

6:以降のsayではハッシュのキーをソートし、joinで空白を挟んで表示している。
ハッシュ要素、ハッシュスライスを削除しているが、ちゃんと抹消されている。

さて、上のプログラムの末尾に変な行を書いてみる。
my $scalar = 666;

delete $scalar;
スカラー変数をdelete関数で消そうとしているのだが、どうなるだろうか。
$ deleteExp.pl
delete argument is not a HASH or ARRAY element or slice at /Users/query1000/perl/deleteExp.pl line 49.
$
やったー。
ちゃんと予想通りのエラーが表示された。

Hashed Beef