Perlのエラーメッセージのコーナー。
Perlのエラーメッセージが表示される最小のプログラムを書いて、どういうときにどう怒られるかを研究する。

今回のお題は
Assignment to both a list and a scalar

である。

Flamingo Assignment
これは文ではなく名詞句である。
「リストとスカラー、両方への代入が起こった」という意味だ。

リストとスカラーとコンテキスト

スカラー(scalar)とは単一の値(数値や文字列)を返す式のことである。
1、3.14、"あいうえお"のような直定数(リテラル)や、$numのようなスカラー変数がこれに入る。
$というシジル(ファニー文字)はScalarのSを取っていると考えると覚えやすい。

scalarという単語は英語で読むとスケイラーになる。
これは数学用語のドイツ語読みに引き摺られて日本ではスカラーと読まれている。

Perlの他にUnicode scalar value(Unicodeの背番号。aはU+0041など)もUnicodeスカラー値と呼ぶことが多い。
これもUnicodeスケイラー値と呼ぶほうが英語の実音に近いと思うが、UnicodeコンソーシアムのターミノロジーでもUnicodeスカラ値となっている。

Unicode Terminology: English - Japanese

リスト(list)は複数のスカラーをカンマで結合したもので、普通カッコに入れて使う。
(1, 2, 3)とか("love", "peace", "forever")とか(6, 5, "Monday")とかである。

Perlはそのままでは二重配列は作れない。
二重配列を作るにはリストのリファレンスというスカラーを作ってリスト要素とする。

リストはスカラーコンテキストとリストコンテキストを持つ。
以下のしょうもないプログラムを考える。
#! /usr/bin/perl
#
# contexts.pl

use strict;
use warnings;
use 5.10.0;

my @arr = (1, 4, 8); #リストコンテキストの代入

my $scl = @arr; #スカラーコンテキストの代入

say "\@arr is @arr and \$scl is $scl";
実行してみる。
$ contexts.pl
@arr is 1 4 8 and $scl is 3
配列変数@arrにはリストコンテキストの代入が起こりリスト(1, 4, 8)が代入された。
スカラー変数$sclにはスカラーコンテキストの代入が起こりスカラー3が代入された。
ここで3はリストの要素数である。

さて、配列変数にスカラーの代入が出来るだろうか。
出来る。
#! /usr/bin/perl
#
# contexts.pl

use strict;
use warnings;
use 5.10.0;

my @arr = (1, 4, 8); #リストコンテキストの代入

my @arr2 = 3; #スカラーのリストコンテキストの代入
push @arr2, 4;

say "\@arr is @arr and \@arr2 is @arr2";
実行する。
$ contexts.pl
@arr is 1 4 8 and @arr2 is 3 4
配列@arr2にスカラー値3を入れると、ただ1つの要素を持つリスト(3)で初期化される。
その後@arr2に4をpushすると、@arr2は2要素のリスト(3, 4)になる。
へー。

さて、配列変数にリストをスカラーコンテキストで代入できる。
何言ってるか分かりますか。
#! /usr/bin/perl
#
# contexts.pl

use strict;
use warnings;
use 5.10.0;

my @arr = (1, 4, 8); #リストコンテキストの代入

my @arr2 = @arr + 0; #スカラーのリストコンテキストの代入
push @arr2, 4;

say "\@arr is @arr and \@arr2 is @arr2";
これでも同じ結果になる。
$ contexts.pl
@arr is 1 4 8 and @arr2 is 3 4
@arr2に@arrを代入するとき、数値ゼロを加算しているので、スカラーコンテキストになり、要素数3が得られるが、それがリスト(3)として代入され、その結果@arr2は(3)になる。

ただ、ゼロを足すのはいかにも分かりにくい.
scalar関数というのを使うと、リストのスカラーコンテキストでの解釈が強制される。
#! /usr/bin/perl
#
# contexts.pl

use strict;
use warnings;
use 5.10.0;

my @arr = (1, 4, 8); #リストコンテキストの代入

my @arr2 = scalar @arr; #スカラーのリストコンテキストの代入
push @arr2, 4;

say "\@arr is @arr and \@arr2 is @arr2";
これでも同じ結果になる。
ぐっと見やすい。
そうでもないですか。

条件演算子

条件演算子「?:」というのがある。
条件 ? 成立するときの値 : 不成立の時の値
と書く。

C言語でも使えるもので、いかにも頭がいい人が使うっぽい。
ぼくは使わない。
if (条件) {
 成立するときの行動;
} else {
 不成立の時の行動;
}
と書く。

たとえば、以下のようなプログラムを考える。
#! /usr/bin/perl
#
# conditions.pl

use strict;
use warnings;
use 5.10.0;

my $num = shift;
my $oddEven;

if ($num % 2 != 0) {
 $oddEven = "奇数";
} else {
 $oddEven = "偶数";
}

say "$numを入力したね。それは$oddEvenだね";
実行する。
引数を1個取ってそれが奇数か偶数かを当てる。
$ conditions.pl 1
1を入力したね。それは奇数だね
$ conditions.pl 2
2を入力したね。それは偶数だね
できてるー。
でも、いかにも冗長である。
これを条件演算子を使うと短く書けるという。
#! /usr/bin/perl
#
# conditions.pl

use strict;
use warnings;
use 5.10.0;

my $num = shift;
my $oddEven = ($num % 2 != 0) ? "奇数" : "偶数";
say "$numを入力したね。それは$oddEvenだね";
実行してみる。
$ conditions.pl 20
20を入力したね。それは偶数だね
$ conditions.pl 23
23を入力したね。それは奇数だね
同じ動作で、たしかにグッと短くなった。

リストを返す条件演算子

このように条件演算子は2つの値になるが、上のはその値が両方「奇数」、「偶数」というスカラーのパターンであった。
両方リストのパターンは以下のようなものである。
#! /usr/bin/perl
#
# conditions.pl

use strict;
use warnings;
use 5.10.0;

my $num = shift;
my @oddEven = ($num % 2 != 0) ? ("2で割れない", "奇数") : ("2の倍数である", "偶数");
say "$numを入力したね。それは@oddEvenだね";
実行する。
$ conditions.pl 13
13を入力したね。それは2で割れない 奇数だね
$ conditions.pl 14
14を入力したね。それは2の倍数である 偶数だね
この場合、代入はスカラーコンテキストで行われる。
では、条件が満たされるときはスカラーを、満たされない時はリストを渡すとどうなるだろうか。
#! /usr/bin/perl
#
# conditions.pl

use strict;
use warnings;
use 5.10.0;

my $num = shift;
my @oddEven = ($num % 2 != 0) ? "奇数" : ("2の倍数である", "偶数");
say "$numを入力したね。それは@oddEvenだね";
これでもうまくいく。
$ conditions.pl 15
15を入力したね。それは奇数だね
[perl]$ conditions.pl 16
16を入力したね。それは2の倍数である 偶数だね
この場合は、スカラー "奇数" を配列に代入すると、("奇数")という1要素のリストになる。
受け側が配列なので、代入はリストコンテキストで行われるので、不明瞭ではない。

条件演算子が左辺に来る

問題は条件演算子が左辺、代入される側に来る場合である。
#! /usr/bin/perl
#
# conditions.pl

use strict;
use warnings;
use 5.10.0;

my $num = shift;
my $odd = "ではない";
my $even = "ではない";

(($num % 2 != 0) ? $odd : $even) = "である"
say "$numを入力したね。それは奇数$oddし、偶数$evenよ!";
この場合は、$numが奇数であれば$oddに、偶数であれば$evenに「である」という文字列が入る。
$ conditions.pl 101
101を入力したね。それは奇数であるし、偶数ではないよ!
$ conditions.pl 102
102を入力したね。それは奇数ではないし、偶数であるよ!
問題は、一方をスカラー、他方をリストにした場合である。
#! /usr/bin/perl
#
# conditions.pl

use strict;
use warnings;
use 5.10.0;

my $num = shift;
my $odd = "ではない";
my @even = "ではない";

(($num % 2 != 0) ? $odd : @even) = "である";
say "$numを入力したね。それは奇数$oddし、偶数@evenよ!";
これがうまくいかない。
$ conditions.pl 2001
Assignment to both a list and a scalar at /Users/query1000/perl/conditions.pl line 13, near ""である";"
Execution of /Users/query1000/perl/conditions.pl aborted due to compilation errors.
$ conditions.pl 2002
Assignment to both a list and a scalar at /Users/query1000/perl/conditions.pl line 13, near ""である";"
Execution of /Users/query1000/perl/conditions.pl aborted due to compilation errors.
これがお題のエラーメッセージである。
上のプログラムでは、スカラー$oddか、配列@evenか、どちらに対して代入が行われるのか分からない。
つまり、この代入は、スカラーコンテキストで行われるのか、リストコンテキストになるのか分からないから、エラーを出して終了する。
実行時に判断してもいいような気がするが、それはできないようだ。