2010/10/09 18:28:36
モナドを使ったプログラミング
持ち上げの一般化(15.2)
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
Prelude Control.Monad> :t liftM
liftM :: (Monad m) => (a1 -> r) -> m a1 -> m r
Prelude Control.Monad> :t ap
ap :: (Monad m) => m (a -> b) -> m a -> m b
liftM(N)はliftMとapを使って一般化できる。
Readerモナドを大体理解した(15.6)。
newtype Reader e a = R { runReader :: e -> a }
instance Monad (Reader e) where
return a = R $ \_ -> a
m >>= k = R $ \r -> runReader (k (runReader m r)) r
ask :: Reader e e
ask = R id
askで得た環境を引き回す。
*Main Data.Char> runReader (ask >>= \x -> return (x*3)) 2
6
askで常に同じ環境が返ってくる、読み取り専用のStateみたいなもんか。
2007/01/03 20:22:37
普通のhaskellプログラミングの216ページに
ポイントフリースタイルへの書き換えは脊髄反射の速度でできるようにしてください。
と書いてあったのだけど、脊髄どころか反射すらできない。
firstNLines :: Int -> String - String
firstNLines n = unlines . take n . lines
nを押し出すのってどうやるんだろうか?
追記07.01.03
解決したヨ。
2007/01/03 20:18:01
下の式を完全なポイントフリースタイルにする方法を色々と考えてみたんだけど。
firstNLines n = unlines . take n . lines
結局分からんかったので、mixiで聞いてみたらさくっと教えてもらえた(ありがたい)。
unlines . take n . lines
(unlines . take n) . lines
(. lines) (unlines . take n)
(. lines) ((unlines .) (take n))
((. lines) . (unlines .)) (take n)
((. lines) . (unlines .)) . take
(. lines) . (unlines .) . take
.って演算子として考えればよいらしい。だから、左側を取るように書き換えてやればいいのか。
Prelude> 5 + 7
12
Prelude> (+ 7) 5
12
(+)だとこんな感じ。
型を確認すると
Prelude> :type (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
.は(b -> c)と(a -> b)という関数をとってa->cという関数を合成する関数(二項演算子?)なのか。
(.) :: (b -> c) -> (a -> b) -> (a -> c)
関数合成で、変数が消えていくのは、こういうことなのね。
2006/09/25 22:32:35
モナドで躓いて以来、ちょっとご無沙汰していたhaskellだが、ちょっとまとまった時間ができたので、この週末でじっくり考えてみた。
モナドだけに、読むたび必ず睡魔に襲われる(参照透明性確保!)でも理解度はちょっぴり向上してるっぽい(つまり副作用)。
檜山正幸のキマイラ飼育記はjavascriptを例にして、純関数にすること(参照透明性の確保?)と型を揃えることが重要っぽいあたりが理解できた。
で、HaHaHa!では思考をもうチョイ広げて、とりまく環境の変化ということに思いをめぐらせればいいということを理解した。特に図がわかりやすくて、かなり理解が進んだと思う。
で、それから、実際にperlの例を見るとを~とか思う。
変数の代入をIOと考えればtieって参照透明性を確保しつつ、副作用を発生させることができるのね。この場合にいわゆるモナド則は成り立つのかなと考えてみたけど、なんとなく成り立つのかなと思ったり。
と、ここらへんまで理解した気になって、再度ふつうのhaskellを。
なんとなくわかってキテルっぽいが、じゃぁ、MaybeモナドとかListモナドって何なのヨっていうか、モナドって一体何さ?っていうあたりがわからなくなってきた。さらにperlのtieなんかで実装できるってことは、モナド則っていうのは結局なんなのってのがよくわからん。
ふつうのhaskellに
モナドを定義の観点からみるならばモナド則がすべて
とあるように、モナド則を感覚で理解しなければならんのかな。
All About Monads
ほんとうにモナドを理解するには、モナドを使ったコードを経験することが最良 の方法です。
というわけで、コードを書こう。
2006/08/19 18:55:01
コメントでhaskellでフィボナッチの別解をもらったが、パッと見てもなぜこれでフィボナッチ数列ができるのかいまいちわからんかったので、よく読んでみたヨ。
まずは型チェック。
見ればわかるとおりfibはリストを返す関数ですな。つまり、fibを実行するとフィボナッチ数がどんどこどんどことそれこそとめどなく溢れてくる(はず)
fib :: Num a => [a]
fib = 0:1:zipWith (+) fib (tail fib)
これを fib.hsという名前で保存してghciで実行してみる。
$ghci
*Main> :l fib.hs
*Main> fib
をを~。凄い勢いでフィボナッチ数列がどんどこど(以下略)。ちなみにlengthでリストの長さをはかってみると、プロンプトがかえってこないゾ。さすが無限リスト。
zipWithの型とtailの型はこんな感じ。
Prelude> :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
Prelude> :t tail
tail :: [a] -> [a]
zipWithは高階関数で、+関数をfibとtail fibに適用して新しいリストをつくるわけですな。tailは先頭の要素を取り除いたリストを返す関数だから、、、うーんやってることはわかるんだけど、イマイチイメージできん。
と悩んでたら、Wikipediaに書き下されてる表をみつけて納得した。perlで同じことやろうとするとまず有限個の要素をもつ配列があってみたいなところから始まるから、こういう表現はできないな。というわけで、目からうろこっていうか、ちょっと感動した。
ここで考えたようなn-1番目とn番目のフィボナッチ数が与えられたときにn+1番目のフィボナッチ数を求めるということを考えるのではなくて、そもそもフィボナッチ数列とは何かを考えて、それを無限リストとして表現することを考えればよいんですよと。
となると、リスト内包表記も感動するような綺麗さダ。
fibs = 0 : 1 : [ a+b | a <- fibs | b <- tail fibs ]
Haskell - Wikipedia
GHCの並列リスト内包表記(GHCの拡張は特別なコマンドラインフラグによって有効にしなければならない。
とあるのが、なにが問題なんだかわからんので、とりあえず実行してやろう。
fib4.hs:1:15: Illegal parallel list comprehension: use -fglasgow-exts
Failed, modules loaded: none.
デフォルトだと、同じリストが複数でてくるような表記の仕方(この場合はfibs)は問題あるということなのかな
とオプションを指定して実行することで、OK。をを~。凄い勢いでフィボナッチ数列がどんどこど(以下略)。
haskell素敵過ぎ
2006/08/10 22:44:11
普通のhaskellプログラミングを読み終えて、なんか次に読むものないかなと探していたら、webarchiveに面白そうなページがあることを知ったので、ダウンロードして読んでます。
3回目の講義資料に、タプルを使って高速にフィボナッチ数を計算する話があったので、なんでそうなるのかを理解するために手で書き下してみた。
普通に書くとこんな感じ(fib)。
fact :: Int -> Int
fact n
| n == 0 = 1
| n > 0 = fact (n-1) * n
書き下すと、計算量があっという間に増えていくことがわかる。
fib 5
=> fib 3 + fib 4
=> (fib 1 + fib 2) + (fib 2 + fib 3)
=> (fib1 + (fib 0 + fib 1)) + ((fib 0 + fib 1) + (fib 1 + fib 2))
=> (fib1 + (fib 0 + fib 1)) + ((fib 0 + fib 1) + (fib 1 + (fib 0 + fib 1)))
タプルを使った例(fastfib)
フィボナッチ数列を効率良く計算する方法を考えよう.前に見たとおり,フィボナッチ数列の各項は,その前の二つの項の和である.逆に言えば,二つの連続した項の値が与えられれば,次の項を計算するのは簡単なのだ.
fibStep :: (Int, Int) -> (Int, Int)
fibStep (u, v) = (v, u+v)
fibPair :: Int -> (Int, Int)
fibPair n
| n == 0 = (0, 1)
| otherwise = fibStep (fibPair (n-1))
fastFib :: Int -> Int
fastFib = fst . fibPair
コードを眺めただけではなんで速くなるのか実感がわかなかったので、書き下してみる。
fastFib 5
=> fst (fibPair 5)
=> fst (fibStep (fibPair 4))
=> fst (fibStep fibStep (fibPair 3))
=> fst (fibStep fibStep fibStep (fibPair 2))
=> fst (fibStep fibStep fibStep fibStep (fibPair 1))
=> fst (fibStep fibStep fibStep fibStep fibStep (fibPair 0))
=> fst (fibStep fibStep fibStep fibStep fibStep (0, 1))
=> fst (fibStep fibStep fibStep fibStep (1, 1))
=> fst (fibStep fibStep fibStep (1, 2))
=> fst (fibStep fibStep (2, 3))
=> fst (fibStep (3, 5))
=> fst (5, 7)
=> 5
をを、タプルにすることで、計算量が減ってます、自分で展開していくとなるほどとよくわかる。
さて、最近Higher-order Perlを読んでいる僕としては、これをperlで書いてみたくなるわけである。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | #!/usr/bin/perl
use strict;
use warnings;
sub fibStep {
my $n = shift;
return [$n->[1], $n->[0] + $n->[1]];
}
sub fibPair {
my ($n, $fs, $fp) = @_;
return [0,1] if ($n == 0);
return $fs->($fp->($n-1,$fs,$fp))
}
sub fastFib {
my $n = shift;
my $fs = \&fibStep;
my $fp = \&fibPair;
my $fib_num = $fp->($n,$fs,$fp);
return $fib_num->[0];
}
my $number = shift;
print fastFib($number)
|
サブルーチンのリファレンスを引数にとらないといけないのがhaskellと異なるとこかな。あとは書いてて思ったのは関数の型を書くとプログラムの流れが理解しやすいナァと。
実行すると一瞬で計算が終わる(速い!)
実際に処理の流れを追うために-dオプションを付けて実行してsを連打すると、main::fibPairがどんどん実行されて$nが0になったところで今度はmain::fibStepが次々実行されていく様がわかる。
Higher-order Perlの5章にもフィボナッチ数列のことが書いてあるのだが、まだそこまで読み進めていない。(今3章突入したところ)
haskellを勉強しながら、Higher-order Perlを読むと、色々新たな発見があって楽しい。
2006/07/20 22:21:58
ふつうのHaskellプログラミングも、とうとう第三部のWIKIをつくろうの章に突入。
でも、第三部は実際のコードのポイントだけ解説なので、サポートページからコードをダウンロードしてきてつきあわせながら読む感じになるので、Meadowでソースコードを開いて本を読んでる。
で、perlとかjavascriptは色がついてて見やすいのに、、、Meadowにhaskellモードってないのかなぁと思って探したらあった。
これでかなりコードが読みやすくなった。
あと本を最後まで読んでみての感想だが、ふつうのHaskellプログラミングは一部、二部と三部の具体事例の間にはかなり開きがあるような気がした。二部と三部の間に数学的な問題を事例として扱う部もあったほうがいいような。
というわけで、「連載: Haskellプログラミング」なんかを読んで解くといいかもしれん。
2006/07/11 22:34:25
ふつうのHaskellプログラミングも、とうとう11章へ。中盤も佳境をむかえております。
モナドが~~
わかりません、さっぱり。他の言語での例があったので少し読んでみた。
檜山正幸のキマイラ飼育記 - 世界で一番か二番くらいにやさしい「モナド入門」
振り返れば我々は; 関数から副作用を取り除き(汚れ作業はCountupMainに押しつけ)、代わりに戻り値に副作用の意図を詰め込み、それによって失われた関数結合の自由さを関数の拡張により取り戻しました。
javascriptもperlのコードもなんとなくわかったような気にはなったんだけど、ふつうのHaskellプログラミングに戻ると混乱してしまうヨ。
週末にでも幾つかコードを書いてみてよく考えることにしよう。
http://del.icio.us/kzfm/haskell
2006/07/03 21:56:55
本日普通のHaskellプログラミングの7章まで読み終えた。そろそろ、具体的な例題解いてみたいなぁと思ったが、3部13章のWIkiをつくってみようの章まではあと5つもある。今のとこ、ここまで読んで、実用Perlプログラミングみたいに他の言語との違いみたいなページを割いてくれるとよかったりと思った。それ以外は、unixのコマンドをtailとかheadとかをhaskellで書こうみたいな感じにすすむので理解はしやすいヨ。
実際に、どんな感じの問題に適用すんのがいいのかなぁとか、考えたときに、ゲノムみたいな大きい配列操作すんのに適してるも。もしかしてbioinformaticsでなんかあるかなと探したらあった。
論文あったのでアブスト眺めたけど、DPってhaskell向きなのかな?まだ、いまいちわかってなさげ。
Rによる統計的プログラミング
でRと比較しているのだから統計的な(というより数学的な)記述にむいているのは感覚的にわかるんだけどなぁ。
ということはchemoinformaticsでの利用例あるだろ、とか思うんだが見当たらない。
2006/06/25 23:27:10
先週は電車通勤+歩きで仕事場を行き来したので、電車の中で、英語漬けで英語のトレーニングしてみたり、普通のHaskellプログラミング読んだりと、電車通勤はなかなか有意義なことに気付かされた。特に普通のHaskellプログラミングは第一部を読み終えたことで、高階関数と変数の束縛というものがわかったような気がした?
、、、、したのかどうかまだよくわからんので、自分の理解力を認識するために少し書いてみた。
まず、言葉の定義を。
- 変数の束縛
- (例えば)関数という値があり、それが変数名を束縛している
- 高階関数
- 関数を返したり引数にとったりする関数
- 変数が関数に束縛されているから、関数を変数として扱うことが出来る?
- それとも関数を変数として扱うことことが出来れば高階関数はつくれるのか?
perlでも変数を束縛することが出来れば高階関数など扱えるのかなと。クロージャ使えってことか?と思って調べて見ると面白そうなのが。
(って全部一度は見たことあるじゃん。でもワカンネとか言って放り出した気が、、、、)
ぱるも日記ではカリー関数を作る例が示されていたが、関数を返す高階関数の例ですな。カリー化に関しては、これなど面白かった。
torus solutions!は僕がblosxomをいじくるときにかなり参考にさせてもらったサイトです。
XML を Perl の高階関数で。 : torus solutions!
XML::LibXML で XML を作るときに、 いちいち createElement とか appendChild とか書くのに飽きてきたので、 高階関数版を作ってみた。
これも、関数を返すといってよいのでしょう。
関数を引数にとる高階関数をperlで書けるのだろうか?というのが気になるところだが、関数のリファレンスを引数にとる関数を書けばいいのかなと思ってこれまた調べてみた。
ここまでの関数を返す関数とか関数を引数にとる関数を考えると、perlでの(関数による)変数の束縛ってのは、関数のリファレンスを変数として扱うことでなされると考えてよいのかな。
Higher-order Perlも気になる