Help us understand the problem. What is going on with this article?

各メジャーバージョンの Perl に同梱されている List::Util で使える関数一覧

リスト処理に有用な関数群を収録している Perl モジュール List::Util はコアモジュールであり、原則的にどの Perl 環境でも同梱されています。

とはいえ、Perl のバージョンアップに伴って、同梱されている List::Util もバージョンアップしており、収録されている関数にも差異があります。2010年前後の古い Perl だと外部モジュール List::MoreUtils を別途インストールしないと出来なかったことも、2020年代の新しい Perl であれば List::Util のみで完結することもあります。

以下、Perl のバージョンと、それに同梱されている List::Util のバージョン、そしてその List::Util が提供する関数一覧を表にしてみました。なお Perl 5.8 より前のバージョンの Perl は省略しています。

Perl List::Util List::Util で使える関数一覧
5.8.0 1.07_00 first min max minstr maxstr reduce sum shuffle
5.10.0 1.19 first min max minstr maxstr reduce sum shuffle
5.12.0 1.22 first min max minstr maxstr reduce sum shuffle
5.14.0 1.23 first min max minstr maxstr reduce sum shuffle
5.16.0 1.23 first min max minstr maxstr reduce sum shuffle
5.18.0 1.27 first min max minstr maxstr reduce sum sum0 shuffle
5.20.0 1.38 all any first min max minstr maxstr none notall product reduce sum sum0 shuffle pairmap pairgrep pairfirst pairs pairkeys pairvalues
5.22.0 1.41 all any first min max minstr maxstr none notall product reduce sum sum0 shuffle pairmap pairgrep pairfirst pairs pairkeys pairvalues
5.24.0 1.42_02 all any first min max minstr maxstr none notall product reduce sum sum0 shuffle pairs unpairs pairkeys pairvalues pairmap pairgrep pairfirst
5.26.0 1.46_02 all any first min max minstr maxstr none notall product reduce sum sum0 shuffle uniq uniqnum uniqstr pairs unpairs pairkeys pairvalues pairmap pairgrep pairfirst
5.28.0 1.50 all any first min max minstr maxstr none notall product reduce sum sum0 shuffle uniq uniqnum uniqstr head tail pairs unpairs pairkeys pairvalues pairmap pairgrep pairfirst
5.30.0 1.50 all any first min max minstr maxstr none notall product reduce sum sum0 shuffle uniq uniqnum uniqstr head tail pairs unpairs pairkeys pairvalues pairmap pairgrep pairfirst

私の場合、よく uniq が使いたくて List::MoreUtils を入れたり自分で uniq を書いたりしていたのですが、Perl 5.26 以降だと List::Util が uniq を提供していることが分かります。

余談:上記の表の出し方

上記の表、最初は手で作成しようと思っていたのですが、以下の方針で多少の自動化をしてみました。

  • List::Util の Git リポジトリを clone する
  • 特定の Perl バージョンが同梱している List::Util のバージョンを corelist コマンドで調査する
  • リポジトリ内に lib/List/Util.pm が存在し、そこの @EXPORT_OK を見ると関数一覧がある
  • リポジトリには List::Util のバージョン番号に対応したタグが大体打たれているので、知りたいバージョンに対応したタグで checkout して前述の @EXPORT_OK を抜き出す

List::Util の GitHub リポジトリ を clone、リポジトリの作業ディレクトリトップに移動して、以下のような雑に書いたプログラムを保存、そして実行しました。

list-util-exports.pl
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw(say signatures);
no warnings qw(experimental::signatures);

# vX.Y.Z
use constant VERSION_Y_MIN => 8;
use constant VERSION_Y_MAX => ($^V =~ /^v5\.(\d+)/);

die if !-f "lib/List/Util.pm";

my %special_vtag_mapping = (
    "v1.07_00" => "v1.07",
    "v1.42_02" => "v1.42_002",
    "v1.46_02" => "v1.46",
);

my @versions = list_util_versions();

# say join "\t", @$_ for @versions;
# say "---";

for my $pair_version (@versions) {
    my ($perl_version, $list_util_version) = @$pair_version;
    my $vtag = "v$list_util_version";
    if ( local $_ = $special_vtag_mapping{$vtag} ) {
        $vtag = $_;
    }
    git_checkout($vtag);
    my @methods = list_util_methods();
    printf "%s\t%s\t%s\n",
        $perl_version, $list_util_version, join " ", @methods;

}

sub git_checkout($arg) {
    my $cmd = "git checkout $arg >> debug.log 2>&1";
    my $rv = system $cmd;
    if ( $rv > 0 ) {
        die "command error ($rv): $cmd";
    }
}

sub list_util_versions {
    my @y_versions = (VERSION_Y_MIN .. VERSION_Y_MAX);
    my @versions; # [PERL_VERSION, LU_VERSION], ...
    for my $y ( grep { $_ % 2 == 0 } @y_versions ) {
        my $perl_version = "5.$y.0";
        my $output = capture( 'corelist', '-v', $perl_version, 'List::Util' );
        #printf "%s\t%s\n", $perl_version, $output;
        my ($list_util_version) = $output =~ /(\S+)$/;
        push @versions, [$perl_version, $list_util_version];
    }
    return @versions;
}

sub list_util_methods {
    my $content = slurp("lib/List/Util.pm");
    $content =~ m{
        \@EXPORT_OK
        \s* = \s*
        qw\( (?<exports>.*?) \)
    }sx;
    my $exports = $+{exports} || die;
    my @methods = $exports =~ /(\S+)/g;
    return @methods;
}

sub slurp($filename) {
    open my $fh, '<', $filename or die;
    my @content = <$fh>;
    close $fh;
    return join "", @content;
}

sub capture (@command) {
    my $pid = open my $pipe, '-|', @command;
    my @output = <$pipe>;
    chomp @output;
    close $pipe;
    return wantarray ? @output : join "\n", @output;
}

Markdown のテーブルにするため縦棒を入れたいなと

$ perl list-util.pl | perl -pe 's/\t/ | /g; s/^/| /; s/$/ |/'

という風に実行、そして記事中に貼り付けてテーブル見出しを書き加えたりしました。たぶん年1回の記事更新時にも使えるかな。

付録:各関数の説明

先ほどの表は Perl と List::Util のバージョンが切り口でしたが、各関数を切り口に、その簡単な説明と初出の Perl バージョンを表にしてみました。

なお、同様に Perl 5.8 より前のバージョンは省略しています。Perl 5.8 以前に同梱されていた List::Util で使えた関数も、初出バージョンは 5.8 としています。

  • 概ね第1引数はリストです
    • 解説がない場合はリストです
  • 第1引数の前、間接オブジェクトスロットに無名ブロックまたはサブルーチンリファレンスを取る sort 型の関数について
    • いわゆる foo { $a <=> $b } @listbar \&hint @list といった呼び出し方をされる関数です
    • 解説では必要に応じて単に「(与えられた)ブロック」と書いています
    • 説明の冒頭に記号注釈を書いています
      • ブロックが1つの引数 $_ を取るものは &($_)
      • ブロックが2つの引数 $a および $b を取るものは &($a, $b)
    • ブロックが引数 $_ を取るものは、リストの左側から順番に要素を $_ に代入、そのブロックにある最後に評価された文の評価結果に応じて何かが行われます
  • reduce はセルに入れるには説明が複雑になったので省いていますが、多くのプログラミング言語にある reduce と同様です
    • (I) 最初に呼び出されるブロックでは ($a, $b) = @list[0,1] とする
    • (II) $a $b におけるブロックの評価値を改めて $a とする
    • (III) $list[2]$b とする
    • (IV) II を行う
    • (V) $list[$i] の添字を1増分して III を行う
    • (VI) 添字がリストの要素数を超過する直前まで上記 IV〜V を繰り返す
    • (VII) 最後の $a の結果が reduce 全体の評価結果となる
  • ペア系関数については詳細な解説を省いています
    • 今後の記事更新で追加するかもしれません
  • 詳細は perldoc(英語日本語)を参照下さい
関数 説明 初導入 Perl バージョン
first &($_) ブロックの評価結果が真となる最初の引数リストの要素を返す 5.8
min リストにある数の最小値を返す(数値評価) 5.8
max リストにある数の最大値を返す(数値評価) 5.8
minstr リストにある文字列の辞書順で最も最初のものを返す(文字列評価) 5.8
maxstr リストにある文字列の辞書順で最も最後のものを返す(文字列評価) 5.8
reduce &($a, $b) (別記) 5.8
sum リストの合計値(総和)を返す(数値評価) 5.8
shuffle リストの順序をランダムに入れ替えた新しいリストを返す 5.8
sum0 sum とほぼ同じ。ただし、引数リストが空だった場合、 sumundef を返すが、sum00 を返す 5.18
all &($_) ブロックの評価結果が全ての引数リストの要素で真の場合、真を返す 5.20
any &($_) ブロックの評価結果が何らかの引数リストの要素で真の場合、真を返す 5.20
none &($_) ブロックの評価結果が全ての引数リストの要素で偽の場合、真を返す 5.20
notall &($_) none の別名 5.20
product リストの全ての積の結果(総乗)を返す 5.20
pairmap &($a, $b) map のペアバージョン 5.20
pairgrep &($a, $b) grep のペアバージョン 5.20
pairfirst &($a, $b) first のペアバージョン 5.20
pairs 各要素がペア配列リファレンスであるリストを返す 5.20
pairkeys 与えられたリストをペアリストとみなし、偶数添字の要素のみ抜き出したリストを返す 5.20
pairvalues 与えられたリストをペアリストとみなし、奇数添字の要素のみ抜き出したリストを返す 5.20
unpairs pairgrep などで得られる、各要素がペア配列リファレンスであるリストの配列リファレンスをデリファレンスしたリストを返す 5.24
uniq 重複する要素を省いたリストを返す 5.26
uniqnum 数値として評価した結果、重複する要素を省いたリストを返す 5.26
uniqstr 文字列として評価した結果、重複する要素を省いたリストを返す 5.26
head head $size, @list の形で呼び出され、最大 $size 個で元のリストを左側に切り詰めた新しいリストを返す 5.28
tail tail $size, @list の形で呼び出され、最大 $size 個で元のリストを右側に切り詰めた新しいリストを返す 5.28
xtetsuji
北海道出身で東京都在住の30代男性。主にPerlプログラマー、ときどき数学屋さん。Perl入学式やPerlBeginnersなど、コミュニティ活動も多数。古典派周辺のクラシック音楽やお洒落カフェを好んでいます。あと、路線バスも好き。
http://post.tetsuji.jp/
gaiax
人と人をつなげる Empowering the people to connect.
http://www.gaiax.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした