スキップしてメイン コンテンツに移動

Perl 5.42 が出たので perldelta を読んだ

去る2025年7月2日に Perl 5.42 がリリースされた。ので例によって perldelta を一通り眺めた。

このバージョンは実験的機能である組込みのクラス構文の実装が進展した。 他にもパフォーマンスの改良、組み込み関数・演算子・C レベル API の追加、多数のバグ修正があるが劇的な変化ではなく、発見・修正された脆弱性もかなり限定的な問題なので刺さる機能がなければ急いで移行する必要はあまりないように思われる。

以下主だった新機能の抜粋。

source::encoding プラグマ

ソースコードが特定の文字エンコーディングで記述されていることを宣言するプラグマ。サポートされているエンコーディングは ASCII と UTF-8 のみである。 use source::encoding 'ascii' が宣言された字句的スコープにおいて非 ASCII 文字を記述するとコンパイル時エラーが発生するようになる。use source::encoding 'utf8' は単に use utf8 のシノニムである。

Perl 5 は 2000 年にリリースされたバージョン 5.6 から UTF-8 によるソースコード記述をサポートしているが、後方互換性のため既定では ASCII を前提としており、utf8 プラグマを使わない限り文字列リテラルや RegExp リテラルはバイト列として解釈されるし、識別子にも英数字および '_' しか使うことができない。

識別子はともかく「リテラルは既定でバイト列である」という意味論は極めて誤用しやすい。Unicode 文字列のつもりで渡した値が意図せずバイト列であったために実行時警告・エラーを得た経験は非英語圏のプログラマなら一度ならずあるだろう。

このプラグマはそのような初歩的なバグをコンパイル時に検出することで、Perl プログラムの最も頻出するエラーの一つを実質的に解消しようとしている。 ちなみに use v5.42 すると自動で use source::encoding 'ascii' も有効になるので、今まさに警告を吐いているようなアプリケーションをアップグレードする際は注意が必要である。

any / all 演算子

実験的機能。だが我々はこれを既に知っている。

Scalar::Util にある同名関数の組み込み演算子版である。

パッケージ区切り子としてのアポストロフィが無効化できるようになった

Acme モジュール作者にとっては重大な変更。なおこの機能は元々このバージョンで廃止予定としてアナウンスされていたものが、CPAN 資産の保全などを目的にフィーチャ・フラグでの管理に転換したものである。

Perl のパッケージ区切り子は C++ スタイルの :: だが、実はこれは Perl 5 からの新構文で Perl 4 では ' であった: Foo'Bar'some_func

この構文には文字列補間が意図しない変数を参照する (e.g., "$who's value is ${who::s} value") とか、半端なエディタの編集モードがバグるとかいう問題があるが、より英語的な DSL として (e.g., Test::More が提供していた isn't)、あるいは一発ネタのミソとして便利に濫用されてきた歴史がある。

そのため CPAN には割とこの古い区切り子を使ったモジュールが現存しており、後方互換性のために Perl 5.42 以降のフィーチャ・バンドルを指定しない限りは今後も当面残されることになった模様である。

:writer フィールド属性

実験的な組み込みのクラス構文におけるフィールド変数 (インスタンス変数) に付与できる新たな属性。当然ながらこれも実験的機能である。

宣言したフィールド変数に対応するセッター・メソッドを生成する。デフォルトではフィールド名に set_ 接頭辞を付した PBP スタイルであるが、属性に引数を与えると別名にも設定できる:

field $x :param :writer(mutate_x)

ただし :reader 属性で生成されるアクセサと同名にはできないので注意すること。設定するとより右側で宣言した方のメソッドに上書きされる:

class Foo {
    field $x :reader :writer(x) = 42;
}

my $obj = Foo->new;
$obj->x;  # Too few arguments for subroutine 'Foo::x' (got 0; expected 1)

字句的メソッド宣言

実験的な組込みのクラス構文におけるメソッドの可視性を字句的スコープに絞る宣言。当然ながらこれも実験的以下略。

つまり my sub のメソッド版である。スコープ外からは参照できず、またクラス (パッケージ) のシンボルテーブルに存在しないので通常の名前ベースのメソッド呼び出しを行うと解決に失敗する

これは一般的な OO 言語でいうところのプライベート・メソッドに相当するが、単にアクセス修飾子がどうとかいう問題ではなく、一度スコープが閉じられると動的言語である Perl のリフレクション機能を駆使しても (スクリプト水準では) 参照できない、かなり強烈なカプセル化を提供する。

字句的メソッド呼び出し演算子

字句的メソッドは通常のメソッド呼び出しができないと上で述べた。ではどうやって呼ぶのかというと、メソッドへのハードリファレンスを得てそれを使う。名前解決を介さないので通常の呼び出しより高速である。

そのためのメソッド呼び出し演算子として新たに用意されたのが ->& である:

class RollingCounter {
  field $max :param = builtin::inf;
  field $cnt :param :reader = 0;

  my method incr() {
    $cnt = 0 if ++$cnt > $max;
  }

  method count() {
    my $curr = $cnt;
    $self->&incr;
    $curr;
  }
}

my $counter = RollingCounter->new(max => 3);
say $counter->count for 1 .. 10;

これは要するに my $ref = \&lexical_meth; $self->$ref(...) の略記なので、実は字句的メソッドでない通常の (i.e., パブリック) メソッドに対しても同様に使える。 ただし実行時の名前解決を介さないということは継承・メソッドのオーバーライドによる多態性を放棄することになる点は注意が必要である。

コメント

このブログの人気の投稿

Perl の新 class 構文を使ってみる

Perl 5 のオブジェクト指向機能は基本的には Python の影響を受けたものだが、データを名前空間 (package) に bless する機構だけで Perl 4 以来の名前空間とサブルーチンをそのままクラスとメソッドに転換し第一級のオブジェクト指向システムとした言語設計は驚嘆に価する。 実際この言語のオブジェクトシステムは動的型付言語のオブジェクト指向プログラミングに要求されるおよそあらゆる機能を暗にサポートしており、CPAN には Moose を筆頭とした屋下屋オブジェクトシステムが複数存在しているがその多くは Pure Perl ライブラリである。つまり「やろうと思えば全部手書きで実現できる」わけである。 そういうわけで Perl のオブジェクト指向プログラミングサポートは機能面では (静的型検査の不在という現代的には極めて重大な欠如を除けば) 申し分ないのだが、しかし Moose その他の存在が示しているように一つ明らかな欠点がある。記述の冗長さだ。 コンストラクタを含むあらゆるメソッドは第一引数としてレシーバを受ける単なるサブルーチンとして明示的に書く必要があるし、オブジェクトのインスタンス変数 (a.k.a. プロパティ / データメンバ) は bless されたデータに直接的ないし間接的に プログラマ定義の方法 で格納されるためアクセス手段は実装依存である。これはカプセル化の観点からは望ましい性質だが、他者の書いたクラスを継承するときに問題となる。ある日データ表現を変更した親クラスがリリースされると突然自分の書いた子クラスが実行時エラーを起こすようになるわけだ。 そうならないためにはインスタンス変数へのアクセスに (protected な) アクセサを使う必要があるのだが、そのためには親クラスが明示的にそれらを提供している必要があるし、そもそも Perl にはメソッドのアクセス修飾子というものがないので完全な制御を与えるならばオブジェクトの内部状態がすべて public になってしまう。 そのような事情もあり、特にパフォーマンスが問題にならないようなアプリケーションコードでは Moose のようなリッチな語彙を提供するオブジェクトシステムを使うことが 公式のチュートリアルでも推奨 されてきた。Perl コアのオブジェクトシステムの改良は...

Project Euler - Problem 42

問題 原文 By converting each letter in a word to a number corresponding to its alphabetical position and adding these values we form a word value. For example, the word value for SKY is 19 + 11 + 25 = 55 = t 10 . If the word value is a triangle number then we shall call the word a triangle word. Using words.txt (right click and 'Save Link/Target As...'), a 16K text file containing nearly two-thousand common English words, how many are triangle words? 日本語訳 単語中のアルファベットを数値に変換した後に和をとる. この和を「単語の値」と呼ぶことにする. 例えば SKY は 19 + 11 + 25 = 55 = t 10 である. 単語の値が三角数であるとき, その単語を三角語と呼ぶ. 16Kのテキストファイル word.txt 中に約2000語の英単語が記されている. 三角語はいくつあるか? 解答 42問目! ちょっと拍子抜けするほど簡単です。三角数を片っ端から計算して連想配列に入れておき、文字列の値を計算して照合するだけです。 #!/usr/bin/env perl use strict; use warnings; use feature qw/say/; use List::Util qw/sum/; sub word_value($) { my $offset = ord('A') - 1; sum map { ord($_) - $offset } split //, uc shift; } sub tri_num($) { my $n = shift; $n * ($n + 1) / 2...

Project Euler - Problem 38

問題 原文 What is the largest 1 to 9 pandigital 9-digit number that can be formed as the concatenated product of an integer with (1,2, ... , n) where n > 1? 日本語訳 整数と(1,2,...,n) (n > 1) との連結積として得られる9桁のPandigital数の中で最大のものを答えよ. 解答 乗数が1, 2, ..., n (n > 1)で桁数は9なので、被乗数mは1から9999の範囲です。 範囲が分かれば後は簡単で、m×1から順に積を連結していって、丁度9桁となったときにPandigital数であればいいわけです。 Pandigital数 を定義通り考えれば「1から9のすべての数字が1回以上現れる数」ですが、この問題では桁数の制約からいずれの数字も1回ずつしか現れないため、「同じ数字が重複して出現せず、0が現れない数」に読み替えることができます。Perlだとこちらの方が正規表現マッチングが高速でした。 #!/usr/bin/env perl use strict; use warnings; use feature qw/say/; use List::Util qw/max/; say max map { my $cat = ''; for (my $mul = 1; length $cat < 9; $cat .= $_ * $mul++) {} (length $cat == 9 and $cat !~ /0/ and $cat !~ /(\d).*\1/) ? $cat : () } 1 .. 9999;

Perl 5 to 6 - 列挙型

これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 16 - Enums の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 16 - 列挙型 SYNOPSIS enum bit Bool <False True>; my $value = $arbitrary_value but True; if $value { say "Yes, it's true"; # 表示される } enum Day ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'); if custom_get_date().Day == Day::Sat | Day::Sun { say "Weekend"; } DESCRIPTION 列挙型は用途の広い獣です。定数の列挙からなる低レベルのクラスであり、定数は典型的には整数や文字列です(が任意のものが使えます)。 これらの定数は派生型やメソッド、あるいは通常の値のようにふるまいます。 but 演算子でオブジェクトに結びつけることができ、これによって列挙型を値に「ミックスイン」できます: my $x = $today but Day::Tue; 列挙型の型名を関数のように使うこともでき、引数として値を指定できます: $x = $today but Day($weekday); ...