currying in perl
DESCRIPTION
http://www.slideshare.net/hiratara/hokkaidopm-17020206 の本物の方。TRANSCRIPT
CurryとHokkaido.pm2013.3.9 hiratara
FreakOut
初代ベストエルティニスト賞
“Hokkaido.pm Casual はじめました” by techno_neko
スープCurryが観客の心と胃袋をつかみ、初代ベストエルティニスト賞となりました!おめでとうございました!http://hachiojipm.org/?p=403
“Hokkaido.pm Casual はじめました” by techno_neko
スープCurryが観客の心と胃袋をつかみ、初代ベストエルティニスト賞となりました!おめでとうございました!http://hachiojipm.org/?p=403
Curryがウケるっぽい
currying
カリー化とは
• A × B→Cの関数を、A→(B→C)にする
• hoge($x, $y) みたいな関数をcurried_hoge($x)($y) みたいにする
カリー化と部分適用は違うよ
部分適用とは
• A × B→Cの関数を、B→Cにする
• hoge($x, $y) みたいな関数に3を適用してhoge_3($y) みたいにする
誤用すると色々危ない
curry関数の実装sub curry { my $f = shift; sub { my $x = shift; sub { $f->($x, @_) }; };}
再帰的にカリー化• hoge($x, $y, $z) みたいな関数は
curried_hoge($x)($y, $z) じゃなくcurried_hoge($x)($y)($z) みたいにしたい
• A × B × C→DA→(B × C→D)A→(B→(C→D))
curry_n関数の実装sub curry_n { my ($n, $f) = @_; $n <= 1 ? $f : sub { my $x = shift; my $fx = curry($f)->($x); curry_n($n - 1, $fx); };}
curry_n関数の実装sub curry_n { my ($n, $f) = @_; $n <= 1 ? $f : sub { my $x = shift; my $fx = curry($f)->($x); curry_n($n - 1, $fx); };}
引数の個数が必要
使い方
sub add { $_[0] + $_[1] + $_[2] }
my $x = (curry_n 3, \&add)->(3)(4)(5);
使い方
sub add { $_[0] + $_[1] + $_[2] }
my $x = (curry_n 3, \&add)->(3)(4)(5);まとめたい
カレーをカレーで食べる
新しい関数を作る
*curried_curry_n = curry_n(2, \&curry_n);*curry3 = curried_curry_n(3);
my $x = curry3(\&add)->(3)(4)(5);
新しい関数を作る
*curried_curry_n = curry_n(2, \&curry_n);*curry3 = curried_curry_n(3);
my $x = curry3(\&add)->(3)(4)(5);
curry_nは2引数関数
新しい関数を作る
*curried_curry_n = curry_n(2, \&curry_n);*curry3 = curried_curry_n(3);
my $x = curry3(\&add)->(3)(4)(5);
curry_nは2引数関数
引数の数を部分適用して新しい関数にする
カリー化すると部分適用しやすい
*curry3 = curried_curry_n(3);*curry4 = curried_curry_n(4);*curry5 = curried_curry_n(5);
*{“curry$_”} = curried_curry_n($_) for 3.. 10000000;
ほんとに引数の数ってわからないの?
($$$)
I ♡ function prototyping
プロトタイプを使う
sub parse_proto ($) { my $f = shift; my $proto = prototype $f; $proto =~ /^([&_+*\$]|\\\[[\$@%&*]+\]|\\[\$@%&*])(.+)/ ? ($1, $2) : (undef, $proto);}
プロトタイプを使うsub curry_proto_n ($) { my $f = shift; my ($p, $ps) = parse_proto $f;
! $p ? $f : eval qq/sub ($p) { my \$x = shift; my \$fx = sub ($ps) { \$f->(\$x, \@_) }; curry_proto_n \$fx; }/;}
プロトタイプを使うsub curry_proto_n ($) { my $f = shift; my ($p, $ps) = parse_proto $f;
! $p ? $f : eval qq/sub ($p) { my \$x = shift; my \$fx = sub ($ps) { \$f->(\$x, \@_) }; curry_proto_n \$fx; }/;}
使い方sub add ($$$) { $_[0] + $_[1] + $_[2] }
my $x = (curry_proto_n \&add)->(3)(4)(5);
BEGIN { *add10 = (curry_proto \&add)->(10) }my $x = add10 3, 4;
使い方sub add ($$$) { $_[0] + $_[1] + $_[2] }
my $x = (curry_proto_n \&add)->(3)(4)(5);
BEGIN { *add10 = (curry_proto \&add)->(10) }my $x = add10 3, 4;
かっこなくてもよい
One more function prototyping
Pod::Functions% perl -MPod::Functions -e 'eval { print $_, "\t", +(prototype "CORE::$_"), "\n" } for keys %Flavor'...getgrgid $connect *$dbmopen \%$$link $$rand ;$dbmclose \%msgsnd $$$umask ;$sleep ;$seek *$$
currying builtins
use 5.16.2;my $x = (curry_proto_n \&CORE::substr)->("abcde")(1)(2);
BEGIN { *substr_abcde = (curry_proto \&CORE::substr)->("abcde");}my $y = substr_abcde 1, 2;
currying builtins
use 5.16.2;my $x = (curry_proto_n \&CORE::substr)->("abcde")(1)(2);
BEGIN { *substr_abcde = (curry_proto \&CORE::substr)->("abcde");}my $y = substr_abcde 1, 2;
かっこなくてもよい
まとめ
• カリー化すると部分適用しやすい
• \&CORE::xxx によりさらに便利に
• プロトタイプ使うと隣の同僚にDISられる