ルモーリン
ホーム更新サービス雑談ランドナーコースガイド鉄ゲタ自転車Linuxリンク連絡先

sort関数比較ブロックの$aと$bのスコープ

概要

sortの比較をカスタマイズする時はブロックの中で$aと$bをリストの2要素として使います。 この$aと$bはレキシカルスコープではありません。 sort専用のチェックがあるようなので探ってみました。

きっかけ

このツイートを見て。

元ネタ

ここに書いてあった(おい)。
Perlの組み込み関数 sort の翻訳 - perldoc.jp
サブルーチンのプロトタイプが ($$)の場合、比較する要素は通常のサブルーチンと 同じように @_ の中にリファレンスとして渡されます。 これはプロトタイプなしのサブルーチンより遅いです; この場合は比較のため サブルーチンに渡される二つの要素は、パッケージのグローバル変数 $a と $b で渡されます(次の例を参照してください)。
Perlの組み込み変数 $a の翻訳 - perldoc.jp
sort() を使うときの特殊パッケージ変数です; "sort" in perlfunc を 参照してください。 この特殊性により、$a と $b は、たとえ strict 'vars' プラグマを 使っているときでも (use vars や our() を使って) 宣言する必要が ありません。 これを sort() 比較ブロックや関数で使えるようにしたい場合は、 my $a や my $b としてレキシカル化しないでください。
perldiag - さまざまな Perl 診断メッセージ - perldoc.jp
【注意】この警告はPerlのバージョンによっては、エラーだったり条件が異なるので使用中のPerlバージョンに対応したメッセージをご覧ください。
"my %s" used in sort comparison (W syntax) パッケージ変数 $a と $b はソート比較のために使われます。 $a または $b をソート比較ブロックの中の <=> または cmp 演算子の オペランドとして使いましたが、この変数はその前にレキシカル変数として 宣言されています。 ソート変数をパッケージ名で修飾するか、レキシカル変数の名前を変えてください。
Perlの組み込み関数 our の翻訳 - perldoc.jp
our は単純名を、現在のレキシカルスコープ内で使うために、 現在のパッケージの同じ名前のパッケージ(つまりグローバルな)変数への レキシカルな別名を作ります。

確認コード

こんな感じ。
#!/usr/bin/env perl -w

use utf8;
use strict;
use warnings;

use v5.10;

use Encode::Argv;
use Encode::Locale;

use open IO => ":utf8";

binmode STDIN, ":encoding(console_in)";
binmode STDOUT, ":encoding(console_out)";

$| = 1;

my @array = (1 .. 20);
print "ソート前:@array\n";

my $a = "たこルカは";
my $b = "俺の嫁";
print "変数\$aと\$b:$a$b\n";

my $s = 1;
say "ソート$s";
@array = sort { $b <=> $a; } @array;
say "ソート後:@array";

$s++;
say "ソート$s";
@array = sort { $::b <=> $::a; } @array;
say "ソート後:@array";

$s++;
say "ソート$s";
@array = sort { $main::a cmp $main::b; } @array;
say "ソート後:@array";

$s++;
say "ソート$s";
@array = sort { our ($a, $b); $b cmp $a; } @array;
say "ソート後:@array";

実行結果

例によってWindows(ActivePerl/5.26)とLinux(CentOS/5.24)で同一の結果になることを確認しました。
"my $b" used in sort comparison at sample_54.pl line 28.
"my $a" used in sort comparison at sample_54.pl line 28.
ソート前:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
変数$aと$b:たこルカは俺の嫁
ソート1
Argument "\x{305f}\x{3053}\x{30eb}\x{30ab}..." isn't numeric in numeric comparison (<=>) at sample_54.pl line 28.
Argument "\x{4ffa}\x{306e}\x{5ac1}" isn't numeric in numeric comparison (<=>) at sample_54.pl line 28.
ソート後:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
ソート2
ソート後:20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
ソート3
ソート後:1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9
ソート4
ソート後:9 8 7 6 5 4 3 20 2 19 18 17 16 15 14 13 12 11 10 1

結論

sortの比較ブロックで変数$aと$bの衝突が警告されたら、比較ブロック(サブルーチン)の中でour ($a, $b)しよう。