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

Perl 5 to 6 - 正規表現の逆襲

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

原文は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 19 - 正規表現の逆襲

SYNOPSIS

# 通常のマッチング:
if 'abc' ~~ m/../ {
    say $/;                 # ab
}

# 暗黙的な:sigspace修飾子を使ったマッチング
if 'ab cd ef'  ~~ mm/ (..) ** 2 / {
    say $1;                 # cd
}

# :sigspace修飾子を使った文字列置換
my $x = "abc     defg";
$x ~~ ss/c d/x y/;
say $x;                     # abx     yefg

DESCRIPTION

正規表現の基本はLesson 07で既に述べたので、役に立つ(が体系立っていない)内容をいくらか補足します。

マッチング

正規表現マッチングのためにグラマーを書かないといけない訳ではなく、昔ながらのm/.../はまだ動きます。 新しい兄弟分としてmm/.../形式があり、:sigspace修飾子を暗黙的に有効にします。これは正規表現中の空白を<.ws>ルールで置換するものだったことを思い出して下さい。

このルールのデフォルトは、2つの単語文字で囲まれている(つまりそれぞれが\wにマッチする)場合は\s+にマッチし、さもなくば\s*にマッチします。

文字列置換において:samespace修飾子は<ws>ルールにマッチした空白を保存します。 同様に:samecase及び短縮形の:ii(:iの親戚だからです)修飾子は大文字小文字の違いを保存します。

my $x = 'Abcd';
$x ~~ s:ii/^../foo/;
say $x;                     # Foocd
$x = 'ABC'
$x ~~ s:ii/^../foo/;
say $x                      # FOO

これはモジュールFooをすべてBarに改名したいけれど、例えば環境変数がすべて大文字で書かれているときに非常に便利です。:ii修飾子が大文字小文字の違いを自動で保存してくれます。

これは大文字か小文字かという情報を文字毎にコピーしますが、より頭の良いバージョンもあります; :sigspace(短縮形は:s)修飾子と一緒に使うと、大文字小文字のパターンをソース文字列から探します。 .lc.uc.lc.ucfirstuc.lcfirstlc.capitalize(Str.capitalizeは各単語の最初の文字を大文字にするメソッド)が認識されます。このようなパターンを見つけると置換後の文字列にも適用します。

my $x = 'The Quick Brown Fox';
$x ~~ s :s :ii /brown.*/perl 6 developer/;
# $xは'The Quick Perl 6 Developer'

選択肢

選択肢は相変わらず|で表現されますが、Perl5とは違った意味になります。 選択肢を順番にマッチングしていって最初にマッチしたものを取るのではなく、全選択肢を並列にマッチングして最長一致のものを取ります。

'aaaa' ~~ m/ a | aaa | aa /;
say $/                          # aaa

つまらない変更に見えるかも知れませんがこれは広範囲に影響し、また拡張性のあるグラマーにとって極めて重要です。 Perl6はグラマーを使って解析されるので、++$a++が2つのprefix:<+>トークンではなく1つのトークンとして解析されるのはこのためです。

順番にマッチングする古い方式は||で利用できます:

grammar Math::Expression {
    token value {
        | <number>
        | '(' 
          <expression> 
          [ ')' || { fail("Parenthesis not closed") } ]
    }
    ...
}

{ ... }はクロージャを実行し、その中でのfailの呼び出しは式を失敗させます。 この枝は前のマッチング(ここでは')')が失敗したときだけ呼び出されることが保証されているので、解析中に役立つエラーメッセージを出すのに使えます。

選択肢を書く方法は他にもあり、例えば配列を「展開」すると要素の選択肢としてマッチさせることができます:

$_ = '12 oranges';
my @fruits = <apple organge banana kiwi>;
if m:i:s/ (\d+) (@fruits)s? / {
    say "You've got $0 $1, I've got { $0 + 2 } of them. You lost.";
}

自動的に最長一致にマッチする選択肢の構築方法がもう1つあります: 多重正規表現です。 これはmulti token nameのように書かれるか、protoを使って次のように書かれます:

grammar Perl {
    ...
    proto token sigil { ... }
    token sigil:sym<$> { <sym> }
    token sigil:sym<@> { <sym> }
    token sigil:sym<%> { <sym> }
    ...
    token variable { <sigil> <twigil>? <identifier> }
}

この例はsigilという多重トークンを表しており、これはsymでパラメータ化されています。 短い名前、つまりsigilが使われた場合は全トークンを選択肢としてマッチします。 選択肢を書くのにこれは非常に面倒くさいと思うかも知れませんが、これには'$'|'@'|'%'と書くのに比べてとても大きな利点があります: 多重正規表現は容易に拡張可能です。

grammar AddASigil is Perl {
    token sigil:sym<!> { <sym> }
}
# わあ、新しいシジルを持ったPerl6グラマーができたぞ!

さらに、既存の選択肢を上書きすることもできます:

grammar WeirdSigil is Perl {
    token sigil:sym<$> { '°' }
}

このグラマーではスカラーのシジルは°なので、このグラマーはシジルを探すときは毎回$の代わりに°を探索しますが、コンパイラにはそれにマッチした正規表現がsigil:sym<$>であることが分かります。

次のレッスンでは実際の、Rakudoで動作するグラマーをお見せしましょう。

コメント

このブログの人気の投稿

OCaml で Web フロントエンドを書く

要旨 フロントエンド開発に Elm は堅くて速くてとても良いと思う。昨今の Flux 系アーキテクチャは代数的データ型と相性が良い。ところで工数を減らすためにはバックエンドも同じ言語で書いてあわよくば isomorphic にしてしまいたいところだが、Elm はバックエンドを書くには現状適していない。 OCaml なら js_of_ocaml でエコシステムを丸ごとブラウザに持って来れるのでフロントエンドもバックエンドも無理なく書けるはずである。まず The Elm Architecture を OCaml で実践できるようにするため Caelm というライブラリを書いている。俺の野望はまだまだこれからだ (未完) Elm と TEA について Elm というプログラミング言語がある。いわゆる AltJS の一つである。 ミニマリスティクな ML 系の関数言語で、型推論を持ち、型クラスを持たず、例外機構を持たず、変数の再代入を許さず、正格評価され、代数的データ型を持つ。 言語も小綺麗で良いのだが、何より付属のコアライブラリが体現する The Elm Architecture (TEA) が重要である。 TEA は端的に言えば Flux フロントエンド・アーキテクチャの変種である。同じく Flux の派生である Redux の README に TEA の影響を受けたと書いてあるので知っている人もいるだろう。 ビューなどから非同期に送信される Message (Redux だと Action) を受けて状態 (Model; Redux だと State) を更新すると、それに対応して Virtual DOM が再構築されビューがよしなに再描画され人生を書き換える者もいた——という一方向の流れはいずれにせよ同じである。 差異はオブジェクトではなく関数で構成されていることと、アプリケーション外部との入出力は非同期メッセージである Cmd / Sub を返す規約になっていることくらいだろうか。 後者は面白い特徴で、副作用のある処理はアプリケーションの外で起きて結果だけが Message として非同期に飛んでくるので、内部は純粋に保たれる。つまり Elm アプリケーションが相手にしないといけない入力は今現在のアプリケーションの完全な状態である Model と、時系列イベ...

多分週刊チラシの裏 (Sep 28 - Oct 04, 2020)

Chrome Web Store が有料 Chrome 拡張の取扱を終了 Chrome Web Store で提供されている有料 Chrome 拡張及びアプリ内課金 API の両方が 2021 年 1 月いっぱいで廃止される。 開発者はそれまでに代替となるサードパーティの課金 API に移行し、購入済ライセンスの移行手段も用意する必要がある。 この決定の発表時点で新規の有料ないしアプリ内課金のある Chrome 拡張の新規登録は終了している。実際のところ 2020 年 3 月時点で既に「一時的に」停止されており、その措置が恒久化されただけとの由。 シェルスクリプティングには長いオプションを使え 「短いオプション (e.g., -x ) はコマンドライン上での略記である。スクリプトにおいては自分や将来の同僚のためにも長いオプション (e.g., ---do-something ) を与える方が理解が容易だろう」という主張。 異論の余地なく正論である。 CobWeb - COBOL to WebAssembly Compiler COBOL から WebAssembly へのコンパイラ。いやマジで。 Cloudflare が何を思ったか同社のサーバレス環境である Workers に COBOL 対応を追加した際 の成果物である。 COBOL から C へのトランスレータである GNU COBOL と C コードをコンパイルして WebAssembly を出力する Emscripten から成っており、他の言語に比べて軽量なバイナリを生成するとのこと。 「ウチではそんな風にはやらないんだ (“We don’t do that here”)」 昨今ソフトウェア開発のコミュニティでも Code of Conduct を用意するところが増えてきたが、コミュニティの文化を明文化するのは難しい。 長大な「べからず集」は息苦しいし、肯定的なガイドラインは時に抽象的で実効的に使えない。問題となるようなふるまいの動機が善意であった場合は特にそうだ。 仮に優れたガイドラインがあっても、それに基いて人を実際に咎めるのは骨が折れることである。初中やればコミュニティ内でも疎まれる。 話の分かる相手ならそれでもまだ説得する意義もあるが、Web 上の対話で当事者双方が納得し合っ...

Project Euler - Problem 25

問題 原文 What is the first term in the Fibonacci sequence to contain 1000 digits? 日本語訳 1000桁になる最初の項の番号を答えよ. 解答 Gaucheのストリームライブラリを使ってみました。 (use util.stream) (define fibonacci-sequence (iterator->stream (lambda (yield end) (let loop ((a 1) (b 1)) (yield a) (loop b (+ a b)))))) (define (digits n) (define (digits-1 m acc) (if (< n m) acc (digits-1 (* m 10) (+ acc 1)))) (digits-1 1 0)) (define (solve) (+ 1 (stream-index (lambda (n) (= 1000 (digits n))) fibonacci-sequence))) (define (main argv) (display (solve)) (newline))

Mac から iPhone のカメラを起動して写真を直接取り込める

Via: The Verge ID セルフィーや (物理) 書籍のページスキャンなど携帯電話のカメラを使って写真を取り込むことは日常的な所作になっているが、写真の使い途が何かの申し込み用 Web フォームなどで iPhone より Mac の方が操作し易いときなどは億劫だ。Mac 組込の FaceTime カメラは 720p とか 1080p しかなくて非力すぎ、かといって iPhone で一旦撮影したものを Photos から探して AirDrop するのも面倒である。 実は macOS Mojave / iOS 12 以降には Continuity Camera という機能がある。これを使うと Apple 製の Mac アプリケーションから iPhone / iPad のカメラを起動して、余計な中間コピーを残すことなく写真を Mac に転送できる。 使い方は簡単で、対応している Mac アプリケーションのコンテキストメニューに “Import (or Insert) from iPhone (or iPad)” という項目がある。“Take Photo” だと一枚、“Scan Documents” だと複数の写真を (歪み補正しつつ) 連続で撮影して転送できる。 対応 Mac アプリケーションは Finder のほか iWork (Keynote, Numbers, Pages), Mail, Messages, Notes, TextEdit となっている、のだが実は Preview でも使える。同様にコンテキストメニューあるいは “File” メニューから起動できる。

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

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