アプリ版:「スタンプのみでお礼する」機能のリリースについて

お世話になります。
Ubuntuのアップデート通知がうるさいので、このたび重い腰を上げて22.04.1 LTSにアップデートしました。
これにより自動的にPerlも5.34に上がったようです。
そしたら今まで正常に動いていたperlスクリプトがエラーになってしまいました。
同じエラーを再現するための必要最小限のコードを書き下ろしました。

#!/usr/bin/perl -w
use strict;
my($v, @a) = 0;
@{0 < $v ? $a[0] : $a[1]}{'a', 'b'} = (0, 1)

見にくくてスミマセンが、配列の1要素にハッシュへのリファレンスを代入しています。
これを実行すると、
Can't use an undefined value as a HASH reference at ./try line 4.
とのエラーが発生してしまいます。

しかし、バージョンアップする前(perl5.30か)では、エラーは発生しませんでしたので、少なくともperl5.30では、これは正しいコードと認識しています。

5.30から5.34までの0.04の間にどのような変更があったのでしょうか。
ちなみに変数$vを使わず直接0 < 0と書いたり、条件演算子の一方を配列でなく0 < $v ? 0 : $a[1]のようにリテラル値にすると、なぜかエラーは発生せず、ますます良く分かりません。

エラーが発生しないように、コードを書き替える事は可能ですが、できれば、この挙動の解説をお願いします。

A 回答 (3件)

「先生」と呼ばれるほどの何者でもないんだけど, 確かに数が多いとどうしようか困るなぁ. 自分だったら... 「そういうハッシュへのリファレンス」を返すサブルーチンを作るかなぁ. 例えば


sub 「そういうハッシュを作るぜ」
{
my %h;
@h{'a', ..., 'f'} = @_;
\%h;
}
ってサブルーチンを作ってそれを
(0 < $v ? $a[0] : $a[1]) = 「そういうハッシュを作るぜ」(@$_);
みたいに呼び出す, みたいな.

この質問文の例のように「同じ配列の異なる要素に入れる」というなら
@{$a[0 < $v ? 0 : 1]}{'a', 'b'} = (0, 1);
のようにもできるんだけどね....
    • good
    • 0
この回答へのお礼

ありがとうございます。
サブルーチンを作ったらうまくいきました。
まあ、本件ではたかだか6件のリストを代入するだけですので、サブルーチンを作るほどの件数ではないですかね。
となると、やはり最初に教わった無名ハッシュ生成子を使って1個1個代入するのが、ここでは最もスマートですかね。

@{$a[0 < $v ? 0 : 1]}{'a', 'b'} = (0, 1)
同じ配列なら、このような巧妙な書き方もできるのですね。
この書き方ならエラーは発生しませんでしたし、よりタイプが少なくて好きです。

しかし残念な事に本番では同じ配列ではないため、この書き方はあきらめます。

お礼日時:2022/10/07 15:03

これを書いて (打って) いる時点での Perl の最新バージョンは 5.36.0 なんだけどそれではチェックしてないですよ~と逃げを打っておいて, と.



手元の 5.26.3 と 5.32.1 で試したところ, 前者ではうまく動いたけど後者はアウトだった. なので, 質問文の「少なくともperl5.30では、これは正しいコードと認識しています」が正しいとすれば 5.30 と 5.32.1 の間のどこかで非互換な修正が入ったものと思われる.

とはいえドキュメントを見た限りでは探せなかったのでもうちょっと掘ってみたんだけど, なんとなく「意図しない非互換な修正」もっといえば「つい間違って突っ込んじゃったバグ」のような気がする. 少なくとも手元の 5.32.1 では質問文の
@{0 < $v ? $a[0] : $a[1]}{'a', 'b'} = (0, 1);
の代わりに
@{0 <= $v ? $a[0] : $a[1]}{'a', 'b'} = (0, 1);
とすると動いてしまっている. でも, 「上はダメで下は OK」という「合理的な理由」があるようには思えない.

ただしどこでなにがどうおかしくなっているのかまでは追求できず... というか, 個人的には
@{0 < $v ? $a[0] : $a[1]}{'a', 'b'} = (0, 1);
と同じ動作をするにしても
(0 < $v ? $a[0] : $a[1]) = { a => 0, b => 1 };
なり
$a[0 < $v ? 0 : 1] = { a => 0, b => 1 };
なりと書いてしまうので追求するモチベーションがない.

なお autovivification をつぶすとは考えにくい>#1.
    • good
    • 0
この回答へのお礼

貴重なお時間を割いていただき感激です。
当人の私ですら最新を入れるモチベは無いので当然です。
ウィザード級の腕を持ってしても無理と回答があると、私は満足して前進できるのです。

しかし本番は、リストがもう少し多く(とは言え6個ですが)、
@{0 < $v ? $a[0] : $a[1]}{'a', 'b', 'c', 'd', 'e', 'f'} = @$_
このように書いて、少ないタイプで代入できて気持ちいと思っていたところです。

この場合、先生はどう書きますか。

私がいつも便利だと思ってすすんで使っていた機能はautovivificationと言うのですね。

お礼日時:2022/10/07 06:13

あまり詳しくないので推測となりますが...


リファレンスが未定義のまま使っているのは、そもそもバグですので直しましょう

https://perldoc.jp/docs/perl/5.28.0/perldiag.pod
> Can't use an undefined value as %s reference
> (F) ハードリファレンスやシンボリックリファレンスとして使用する値は、 定義済みの値でなければなりません。 潜伏中のエラーを引きずり出す助けとなります。

ご提示の場合だと @a の初期化にて、各項目に HASH リファレンスを代入しましょう
例) my @a = ( {}, {} );

5.36 に以下の変更が入っていましたので、
5.34 でも診断精度が上がった結果として摘出されたのではないでしょうか

https://perldoc.jp/docs/perl/5.36.0/perl5360delt …
> perl5360delta - perl v5.36.0 での変更点
> 新しいエラー
> Can't modify %s in %s (for scalar assignment to undef)
> undef = $foo; のように、undef にスカラを代入しようとしました ...

余談)
これまで未定義でも動いていたのは自動補正してくれたからですが、このような補正に頼っていてはいけないという -w オプションからの忠告なのでしょう
    • good
    • 0
この回答へのお礼

ご教授に従い、あらかじめ空のハッシュへのリファレンスを定義したら、エラーが発生しなくなりました。

しかし、先のコードをさらに簡略化して、

#!/usr/bin/perl -w
use strict;
my(@a);
@{$a[0]}{'a', 'b'} = (0, 1)

とすると、あらかじめ定義していないのに、エラーは発生しません。
不思議です。

また余談について、もしやと思い、試してみましたが、本件は-wオプションの有無にかかわらず、エラーになるようです。
ついでにuse strict;も無くしてみましたが、エラーはなくなりませんでした。

しかし未定義でも、書き方によってエラーになったり、ならなかったりするので、いまいち納得いかないのです。

お礼日時:2022/10/06 16:40

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!