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

Perl 5 to 6 - カリー化

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

原文は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 28 - カリー化

SYNOPSIS

use v6;

my &f := &substr.assuming('Hello, World');
say f(0, 2);                # He
say f(3, 2);                # lo
say f(7);                   # World

say <a b c>.map: * x 2;     # aabbcc
say <a b c>.map: *.uc;      # ABC
for ^10 {
    print <R G B>.[$_ % *]; # RGBRGBRGBR
}

DESCRIPTION

カリー化、あるいは部分適用とは関数やメソッドにいくつかの引数を与えて関数を生成する処理のことです。 これは打鍵数を節約し、また他の関数にコールバック関数を渡したいときに便利です。

"Hello, World"から簡単に部分文字列を取り出せる関数が欲しいと仮定しましょう。古典的なやり方は専用の関数を書くことです:

sub f(*@a) {
    substr('Hello, World', |@a)
}

assumingによるカリー化

Perl6のコードオブジェクトはassumingメソッドを持ちます。これは呼び出し元のコードオブジェクトに引数を適用し、その部分適用された関数を返します。

my &f := &substr.assuming('Hello, World');

これでf(1, 2)substr('Hello, World', 1, 2)と等価になります。

演算子は単に変な名前のサブルーチンなので、assumingは演算子に対しても使えます。 与えられた数に2を足すサブルーチンが欲しければ次のように書けます

my &add_two := &infix:<+>.assuming(2);

しかしこれは長ったらしすぎるので、別の方法があります。

Whatever-Starによるカリー化

my &add_two := * + 2;
say add_two(4);         # 6

このアスタリスクスはWhateverと呼ばれ、引数のプレースホルダです。したがって式全体はクロージャを返します。 複数のWhateverを1つの式に含めることもでき、その場合*の項を形式的パラメータで置き換え、複数の引数を取るクロージャが作られます。 つまり* * 5 + *-> $a, $b { $a * 5 + $b }と等価です。

my $c = * * 5 + *;
say $c(10, 2);                # 52

2番目の*は項ではなく中置演算子なのでWhateverによるカリー化の対象ではないことに注意して下さい。

Whateverを使った式のクロージャへの昇格は構文に基づいてコンパイル時に行われます。これはつまり

my $star = *;
my $code = $star + 2

はクロージャを生成せず、次のようなメッセージを出してdieするということです。

Can't take numeric value for object of type Whatever

Whateverによるカリー化にはassumingよりも多様な使い方があります。最初の引数以外も簡単にカリー化できるからです:

say  ~(1, 3).map: 'hi' x *    # hi hihihi

これは文字列繰り返し演算子であるinfix:<x>の第2引数をカリー化しているので、数値引数で呼び出されるとその数だけhiを連結した文字列を生成するクロージャを返します。

メソッドの呼び出し元オブジェクトをWhateverにすることもできます。よって

say <a b c>.map: *.uc;      # ABC

は引数に対してucメソッドを呼び出すクロージャになります。

MOTIVATION

Perl5で関数プログラミングができることはMark Jason DominusがHigher Order Perlで明らかにしています。

Perl6はこれをもっと簡単にしようと試みており、関数プログラミングの典型的な構造を作るための使い易いツールを提供しています。 カリー化とクロージャの容易な生成は関数プログラミングの鍵となるもので、例えばmapgrepと組み合わせて使うデータ変換を非常に簡単に書けるようにします。

SEE ALSO

http://perlcabal.org/syn/S02.html#Built-In_Data_Types

http://hop.perl.plover.com/

http://en.wikipedia.org/wiki/Currying

コメント

このブログの人気の投稿

京大テキストコーパスのパーサを書いた

要旨CaboCha やなんかの出力形式であるところの京大テキストコーパス形式のパーサモジュールを Perl で書いたので紹介します。GithubTarball on Github Ppagesこれを使うと例えば CaboCha の出力した係り受け関係を Perl のオブジェクトグラフとして取得できます。使用例単なる文節区切りの例。#!/usr/bin/env perl use v5.18; use utf8; use IPC::Open3; use Parse::KyotoUniversityTextCorpus; use Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab; use Symbol qw//; my ($in, $out, $err); my $pid; BEGIN { ($in, $out, $err) = (Symbol::gensym, Symbol::gensym, Symbol::gensym); $pid = open3($in, $out, $err, cabocha => '-f1'); } END { close $out; close $err; waitpid $pid => 0 if defined $pid; } binmode STDOUT, ':encoding(utf8)'; binmode $in, ':encoding(utf8)'; binmode $out, ':encoding(utf8)'; my $parser = Parse::KyotoUniversityTextCorpus->new( morpheme_parser => Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab->new, ); say $in '星から出るのに、その子は渡り鳥を使ったんだと思う。'; say $in '出る日の朝、自分の星の片付けをした。'; close $in; my $sentence_trees = $parser->…

Algorithm::LibLinear の紹介

Notice: This article is outdated. Please refer an updated English tutorial. 要旨かなり前になりますが、Algorithm::LibLinear という Perl モジュールを書きました。CPANGithubこれを使うと線形分類器などが高速に学習できます。テキストや画像の分類が応用として期待されます。LIBLINEAR についてLIBLINEARLIBSVM と同じ台湾国立大学の Chih-Jen Lin 教授のチームが公開しているオープンソースの機械学習パッケージです。 関数のロジスティック回帰、サポートベクター回帰及び線形 SVM による多クラス分類を行うことができます。LIBSVM と違ってカーネル関数を使うことはできませんが、はるかに高速に動作します。Algorithm::LibLinear についてLIBLINEAR には C++ で書かれたライブラリと、その機能を使って機械学習と分類・関数回帰を行うコマンドラインユーティリティが含まれています。 Algorithm::LibLinear はライブラリの機能を Perl からオブジェクト指向的に利用できるようにした上で、コマンドラインユーティリティの一部機能をライブラリ化して Perl で再実装したものです。使い方分類問題を解くときは、訓練データセットの読み込み・スケーリング学習器パラメータの設定分類器の訓練実データの分類という手順で行います。訓練データセットの読み込み正解ラベルのついたデータを大量に用意して学習させます。LIBSVM 形式のデータを読み込むか:my $data_set = Algorithm::LibLinear::DataSet->load(string => <<'EOD'); 1 1:0.1 2:0.1 4:0.1 -1 1:0.1 2:-0.1 3:0.1 ... EOD HashRef として表現されたデータを使います:my $data_set = Algorithm::LibLinear::DataSet->new(data_set => [ +{ feature => +{ 1 => 0.1, 2 => 0.1, 4 =…

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 と、時系列イベントである Me…