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

Perl 5 to 6 - Unicode

これはMoritz Lenz氏のWebサイトPerlgeek.deで公開されているブログ記事"Perl 5 to 6" Lesson 17 - Unicodeの日本語訳です。

原文は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 17 - Unicode

SYNOPSIS

(なし)

DESCRIPTION

Perl5のUnicodeモデルは大きな弱点に悩んでいました: バイナリとテキストデータに同じ型を使っていたのです。 プログラムがネットワークソケットから512バイト読み込んだとすると、それは当然バイト列になります。しかしそれに対して(Perl5で)ucを呼ぶとテキストとして扱われます。 推奨されている方法は最初にバイト列をデコードすることですが、サブルーチンがそれを引数として受け取る段階では、それがエンコードされているのかどうか、つまりblobとして扱うべきかテキストとして扱うべきか確実に判断することは不可能です。

一方、Perl6は単なるバイトのコレクションであるBuf型を提供します。またStrは論理的な文字のコレクションです。

論理的な文字という用語にはいささか説明が必要です。正確に言うと、Strは様々な水準の見方ができるオブジェクトです: ByteCodePoint(Unicodeコンソーシアムが番号を割り当てたものすべて)、Grapheme(書記素; 文字として目に見えるもの)、CharLingua(言語定義文字)。

例えば16進数表記で61 cc 80は(当然)3バイトのバイト列を構成します。しかしこれは2つのコードポイントLATIN SMALL LETTER A(U+0041)とCOMBINING GRAVE ACCENT(U+0300)、あるいは1つの書記素(ブログシステムないしあなたのブラウザが文字を殺してなければ、àのように見えます)を表しているものと見ることもできます。

したがって文字列の長さを単純に調べることはできず、特定の見方に拠る長さを調べる必要があります:

$str.bytes;
$str.codes;
$str.graphs;

charsメソッドもあり、これは現在のUnicode水準(use byteのようなプラグマで指定でき、デフォルトは書記素)における長さを返します。

Perl5ではしばしば間違ってバイト列を文字列に連結してしまう問題が起きます。 Perl6でそのような問題に悩んだときは、文字列連結演算子をオーバーロードして簡単に問題が発生した位置を特定できます:

sub GLOBAL::infix:<~> is deep (Str $a, Buf $b)|(Buf $b, Str $a) {
    die "Can't concatenate text string «"
        ~ $a.encode("UTF-8")
          "» with byte string «$b»\n";
}

エンコードとデコード

IOシステムの仕様は非常に基本的なもので、エンコードとデコード用のレイヤはまだ定義されていません。この記事にSYNOPSISがないのはそのためです。 そのような機構が用意されるのは間違いなく、コードは次のようなものになると思われます:

my $handle = open($filename, :r, :encoding<UTF-8>);

正規表現とUnicode

正規表現はUnicode水準を指定する修飾子を取ることができ、m:codes/./は丁度1個のコードポイントにマッチします。修飾子を指定しない場合は現在のUnicde水準が使われます。

(文字にマッチする)\wのような文字クラスはUnicode標準にしたがって動作します。 修飾子として大文字小文字の無視(:i)やアクセントの無視(:a)、置換演算子の置換結果に大文字小文字やアクセントを反映するもの(:samecase:sameaccent、省略形は:ii:aa)があります。

MOTIVATION

今日のほとんどのツールやプログラミング言語で文字列を正しく処理するのはとても難しいことです。 Perl5で書かれたWebアプリケーションを作っているとして、長い単語がレイアウトを崩してしまわないように分割したいとしましょう。 組み込みのsubstrを使ったりすると、意図せずして書記素が泣き別れになるかも知れません。

Perl6は書記素レベルの文字列操作を組み込みでサポートした最初の主要言語になります。 この文字列操作機能はUnicodeを使う上でほとんどの悩みを取払い、(正規表現と連携して)Perl6を文字列処理における最も強力な言語の1つにします。

テキスト用とバイト列用の個別のデータ型はデバッグとイントロスペクションを非常に容易にします。

SEE ALSO

http://perlcabal.org/syn/S29.html#Str

コメント

このブログの人気の投稿

BuckleScript が ReScript に改称し独自言語を導入した

Via: BuckleScript Good and Bad News - Psellos OCaml / ReasonML 文法と標準ライブラリを採用した JavaScript トランスパイラである BuckleScript が ReScript に改称した。 公式サイトによると改称の理由は、 Unifying the tools in one coherent platform and core team allows us to build features that wouldn’t be possible in the original BuckleScript + Reason setup. (単一のプラットフォームとコアチームにツールを統合することで従来の BuckleScript + Reason 体制では不可能であった機能開発が可能になる) とのこと。要は Facebook が主導する外部プロジェクトである ReasonML に依存せずに開発を進めていくためにフォークするという話で、Chromium のレンダリングエンジンが Apple の WebKit から Google 主導の Blink に切り替わったのと似た動機である (プログラミング言語の分野でも Object Pascal が Pascal を逸脱して Delphi Language になったとか PLT Scheme (の第一言語) が RnRS とは別路線に舵を切って Racket になったとか、割とよくある話である。) 公式ブログの Q&A によると OCaml / ReasonML 文法のサポートは継続され、既存の BuckleScript プロジェクトは問題なくビルドできるとのこと。ただし現時点で公式ドキュメントは ReScript 文法のみに言及しているなど、サポート水準のティアを分けて ReScript 文法を優遇することで移行を推進していく方針である。 上流である OCaml の更新は取り込み、AST の互換性も維持される。将来 ReScript から言語機能が削除されることは有り得るが、OCaml / ReasonML からは今日の BuckleScript が提供する機能すべてにアクセスできる。 現時点における ReScript の ...

Perl のサブルーチンシグネチャ早見表

Perl のサブルーチン引数といえば実引数への参照を保持する特殊配列 @_ を手続き的に分解するのが長らくの伝統だった。これはシェルの特殊変数 $@ に由来する意味論で、おそらく JavaScript の arguments 変数にも影響を与えている。 すべての Perl サブルーチンはプロトタイプ宣言がない限りリスト演算子なので、この流儀は一種合理的でもあるのだが、実用的にそれで良いかというとまったくそうではないという問題があった; 結局大多数のサブルーチンは定数個の引数を取るので、それを参照する形式的パラメータが宣言できる方が都合が良いのである。 そういうわけで実験的に導入されたサブルーチンシグネチャ機能により形式的パラメータが宣言できるようになったのは Perl 5.20 からである。その後 Perl 5.28 において出現位置がサブルーチン属性の後に移動したことを除けば Perl 5.34 リリース前夜の今まで基本的に変わっておらず、未だに実験的機能のままである。 おまじない シグネチャは前方互換性を持たない (構文的にプロトタイプと衝突している) 実験的機能なのでデフォルトでは無効になっている。 そのため明示的にプラグマで利用を宣言しなければならない: use feature qw/signatures/; no warnings qw/experimental::signatures/; どの途みんな say 関数のために使うので feature プラグマは問題ないだろう。実験的機能を断りなしに使うと怒られるので、 no warnings で確信犯であることをアピールする必要がある。 これでプラグマのスコープにおいてサブルーチンシグネチャ (と :prototype 属性; 後述) が利用可能になり、 従来のプロトタイプ構文が無効になる。 使い方 対訳を載せておく。シグネチャの方は実行時に引数チェックを行うので厳密には等価でないことに注意: # Old School use feature qw/signatures/ 1 sub f { my ($x) = @_; ... } sub f($x) { ... } 2 sub f { my ($x, undef, $y) = @_...

(multi-)term-mode に dirtrack させる zsh の設定

TL;DR .zshrc に以下を書けば良い: # Enable dirtrack on(multi-)term-mode. if [[ " $TERM " = eterm * ]]; then chpwd() { printf '\032/%s\n' " $PWD " } fi 追記 (May 14, 2025): oh-my-zsh を使っていれば emacs プラグインが勝手にやってくれる: plugins = ( emacs ) 仔細 term-mode は Emacs 本体に付属する端末エミュレータである。基本的には Emacs 内でシェルを起動するために使うもので、古い shell-mode よりも端末に近い動きをするので便利なのだが、一つ問題がある。シェル内でディレクトリを移動しても Emacs バッファの PWD がそのままでは追従しない点だ。 こういう追従を Emacs では Directory Tracking (dirtrack) と呼んだりするが、 shell-mode や eshell ではデフォルトで提供しているのに term-mode だけそうではない。 要するにシェル内で cd してもバッファの PWD は開いた時点のもの (基本的には直前にアクティヴだったバッファの PWD を継承する) のままなので、移動したつもりで C-x C-f などをするとパスが違ってアレっとなることになる。 実は term-mode にも dirtrack 機能自体は存在しているのだが、これは シェルがディレクトリ移動を伴うコマンドを実行したときに特定のエスケープシーケンスを含んだ行を印字することで Emacs 側に通知するという仕組み になっている。 Emacs と同じく GNU プロジェクトの成果物である bash は Emacs 内での動作を検出すると自動的にこのような挙動を取るが、zsh は Emacs の事情なんか知ったことではないので手動で設定する必要がある。 まずもって「ディレクトリ移動のコマンドをフックする」必要がある訳だが、zsh の場合これは簡単で cd / pushd / popd のようなディレクトリ移...

Perl 7 より先に Perl 5.34 が出るぞという話

Perl 5 の次期バージョンとして一部後方互換でない変更 (主に間接オブジェクト記法の削除とベストプラクティスのデフォルトでの有効化) を含んだメジャーバージョンアップである Perl 7 がアナウンスされたのは昨年の 6 月 のことだったが、その前に Perl 5 の次期周期リリースである Perl 5.34 が 5 月にリリース予定 である。 現在開発版は Perl 5.33.8 がリリースされておりユーザから見える変更は凍結、4 月下旬の 5.33.9 で全コードが凍結され 5 月下旬に 5.34.0 としてリリース予定とのこと。 そういうわけで事前に新機能の予習をしておく。 8進数数値リテラルの新構文 見た瞬間「マジかよ」と口に出た。これまで Perl はプレフィクス 0 がついた数値リテラルを8進数と見做してきたが、プレフィクスに 0o (zero, small o) も使えるようになる。 もちろんこれは2進数リテラルの 0b や 16進数リテラルの 0x との一貫性のためである。リテラルと同じ解釈で文字列を数値に変換する組み込み関数 oct も` 新構文を解するようになる。 昨今無数の言語に取り入れられているリテラル記法ではあるが、この記法の問題は o (small o) と 0 (zero) の区別が難しいことで、より悪いことに大文字も合法である: 0O755 Try / Catch 構文 Perl 5 のリリース以来 30 年ほど待たれた実験的「新機能」である。 Perl 5 における例外処理が特別な構文でなかったのは予約語を増やさない配慮だったはずだが、TryCatch とか Try::Tiny のようなモジュールが氾濫して当初の意図が無意味になったというのもあるかも知れない。 use feature qw/ try / ; no warnings qw/ experimental::try / ; try { failable_operation(); } catch ( $e ) { recover_from_error( $e ); } Raku (former Perl 6) だと CATCH (大文字なことに注意) ブロックが自分の宣言されたスコープ内で投げられた例外を捕らえる...

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

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