無駄と文化

実用的ブログ

Perl のショートカット集 (随時更新)

Perl には頻出するイデオムを短い書き方で代替する ショートカット が多くあります。
ショートカットを集めてみます。

目次

while ループの条件部に行入力演算子 <STDIN> を置く

while (<STDIN>) {
    print;
}

下記のコードと同じ意味になります。

while (defined($_ = <STDIN>)) {
    print $_;
}

いま標準入力 STDIN からの読み込みを例に挙げましたが、そのほかのファイルハンドラからの読み込みでも同じように扱われます。

foreach ループで制御変数を省略する

foreach (@array) {
    print;
}

下記のコードと同じ意味になります。

foreach my $_ (@array) {
    print $_;
}

foreach ループにおいて制御変数を省略すると、配列の各値が暗黙に $_ に代入されます。

正規表現リテラルを単体で評価する

正規表現マッチは =~ 演算子を使って、

$str =~ /^(.*)$/;

と書かれることが多いですが、 正規表現リテラルを単体で書いたときには m 演算子が省略されていると解釈されます。
そして m 演算子はデフォルトで $_ とのマッチを試みます。

my $matched = /^(.*)$/;

if (/^(.*)$/) {
    print $1;
}

&some_func(/^(.*)$/);

下記のコードと同じ意味になります。

my $matched = $_ =~ m/^(.*)$/;

if ($_ =~ m/^(.*)$/) {
    print $1;
}

&some_func($_ =~ m/^(.*)$/);
# パターンそのものがサブルーチンに渡されるわけではないので注意!

ちなみに、パターンそのものを変数に格納したりサブルーチンの引数として渡したいときには正規表現オブジェクトを使用します。

# 変数に格納できる
my $regex = rq/^(.*)$/;

# 引数として渡せる
&some_func(rq/^(.*)$/);

=>, のシノニム

=>, のシノニムです。
なのでコードの中で , を使っている箇所を => で置き換えることが可能です。

=> は左側のオペランドを文字列としてクォートするので厳密に云うとちょっとだけ振る舞いが違う

my %hash = ( foo => 'bar', hoge => 'fuga' );

my @array = ( 'foo', 'bar', 'hoge', 'fuga' );

print "hoo", "\n", "bar", "\n";

下記のコードと同じ意味になります。

my %hash = ( 'foo', 'bar', 'hoge', 'fuga' );

my @array = ( foo => 'bar', hoge => 'fuga' );

print "hoo", "\n" => "bar", "\n";

ただし、はっきり云って可読性を落とすだけなので闇雲に ,=> で置き換えることはやめましょう。
=> を使うのはハッシュコンテキストで評価される値を作るときだけにしておくのが無難です。

デリファレンスのチェーン

HashRef をデリファレンスして要素アクセスする ->{} 演算子や ArrayRef をデリファレンスして要素アクセスする ->[] 演算子が連なっているときは間の -> を省略可能です。

$foo->{bar}[0][2]{hoge};

下記のコードと同じ意味になります。

$foo->{bar}->[0]->[2]->{hoge};

まとめ

(たぶんまだ追記します)

 

私からは以上です。

history.back() でページを戻ったときの JavaScript の挙動

「ページ A」から「ページ B」へ遷移した後に「ページ B」で hidtory.back() が実行され「ページ A」に戻ってきたとき、「ページ A」で走っていたスクリプトの挙動について考えよう。

f:id:todays_mitsui:20220214191042p:plain

ちなみにブラウザの「戻る」ボタンでページを戻ったときにも全く同じ議論が通じる。

 

TL;DR

  • ページ A で window.addEventListener('unload', ...) されているときは、JavaScript の状態はリセットされ再実行される
  • ページ A で window.addEventListener('unload', ...) されていないときは、JavaScript の状態はリセットされずに再開される
  • 下記の参考ページを読んで

nmi.jp

 

hidtory.back() 後に JavaScript の挙動おかしくなりがち

長年 hidtory.back() 後の JavaScript の挙動に悩まされてきた。

例えばあるとき、 Vue.js 製のフォームで hidtory.back() 後にフォームの入力がリセットされていることが問題になった。
しかも厄介なのは、この問題が再現する場合としない場合があった。

というわけで本腰入れて調べた結果、上記に挙げた参考サイトにたどり着いた。
ページ A に window.addEventListener('unload', ...) があるか否かによって hidtory.back() 後のスクリプトの挙動が変わる。

 

window.addEventListener('unload', ...) があるとき

hidtory.back() 後には JavaScript の状態がリセットされて、最初からスクリプトが再実行される。
Vue.js や React のような 要素を動的に作るライブラリ を使用している場合、状態をリセットして <input> タグを生成しなおすので入力内容は消えてしまう。

 

window.addEventListener('unload', ...) が無いとき

hidtory.back() 後に JavaScript の状態リセットはされず、ページ遷移した直後からスクリプトが再開するように見える。
<input> タグが生成された後の状態から再開するので入力内容は生きる。

 

ちなみに

window.addEventListener('unload', ...) ではなく、古き良き window.onunload = function() { ... } の形でリスナーを設定していても、同じ挙動になる。

 

誰が window.addEventListener('unload', ...) していたのか

個人の観測範囲では、 unload イベントにリスナーを設定することは滅多にしないことだ。
私の手元の環境では Facebook ピクセルwindow.addEventListener('unload', ...) していた。そのため Facebook 広告を配信しているか否かによって hidtory.back() 後の挙動が違って見えていた。

 

まとめ

挙動がまちまちだと問題になるが、挙動を均一化してしまえば何らかの対処ができると思う。
私の場合は問題がおこるページに window.addEventListener('unload', () => {}) を設定し、 window.addEventListener('unload', ...) があるときの挙動に寄せて対処した。

ほんと勘弁して。

 

私からは以上です。