[表示 : 全て 最新50 1-99 101- 201- 301- 401- 501- 601- 701- 801- 901- 2chのread.cgiへ]
Update time : 10/27 09:49 / Filesize : 225 KB / Number-of Response : 933
[このスレッドの書き込みを削除する]
[+板 最近立ったスレ&熱いスレ一覧 : +板 最近立ったスレ/記者別一覧] [類似スレッド一覧]


↑キャッシュ検索、類似スレ動作を修正しました、ご迷惑をお掛けしました

関数型プログラミング言語Haskell Part9



1 名前:デフォルトの名無しさん mailto:sage [2008/05/17(土) 16:41:29 ]
haskell.org
www.haskell.org/

日本語サイト
www.sampou.org/cgi-bin/haskell.cgi
www.shido.info/hs/

過去ログ
関数型プログラミング言語Haskell
Part1 pc.2ch.net/tech/kako/996/996131288.html
Part2 pc2.2ch.net/test/read.cgi/tech/1013846140/
Part3 pc8.2ch.net/test/read.cgi/tech/1076418993/
Part4 pc8.2ch.net/test/read.cgi/tech/1140717775/
Part5 pc8.2ch.net/test/read.cgi/tech/1149263630/
Part6 pc11.2ch.net/test/read.cgi/tech/1162902266/
Part7 pc11.2ch.net/test/read.cgi/tech/1174211797/
Part8 pc11.2ch.net/test/read.cgi/tech/1193743693/
・2chの仕様により、行頭の半角スペースは表示されません。
 コードをインデントしたいときは、代わりに または全角スペースを使うことができます。


657 名前:デフォルトの名無しさん mailto:sage [2008/08/29(金) 17:28:28 ]
>>654
ReadP の計算で左結合になってる >>= がある場合でも、
内側の P の >>= をすべて右結合にすることで、
P の >>= の再帰が無くなって効率が良くなる。

9節の第1パラグラフに書いてある通りなんだけど。

左結合を右結合にってのは、>>= の結合則
(m >>= f) >>= g == m >>= (\a -> f a >>= g)
の左辺を右辺にするってな話。
例えば、string s >>= f でも string s の中で
>>= を使ってるので、左結合になってる。
つまりほとんど全ての場合に当てはまる。

658 名前:デフォルトの名無しさん mailto:sage [2008/08/29(金) 17:29:19 ]
効率が悪くなる事情は、そこにも書いてあるようにリストの ++ と同じ。

リストの ++ は左引数に関して再帰する。
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
そのため (xs ++ ys) ++ zs は xs に関して二重に再帰することになる。
foldr (++) [] (map show [1..10000])
foldl (++) [] (map show [1..10000])
実際これらを実行してみると前者はすぐ終わるけど、後者は "1" を10000回結合、
"2" を9999回結合、... "10000" を1回結合、みたいになって遅い。加速してくけど。
遅いだけじゃなく、中間リストを生成するので無駄にメモリを使うことにもなる。
foldl は極端な例だけど、foldr も極端で、いつも無駄が無いようにはいかない。

659 名前:デフォルトの名無しさん mailto:sage [2008/08/29(金) 17:30:18 ]
で、回避策。

xs ++ ys は、xs の最後の [] を ys に置き換える。
それを効率よくやるには、最初っから [] なんか使わないで、
1:2:3:[] を \nil -> 1:2:3:nil みたいにしとけばいいじゃんという発想。
つまり [a] を [a] -> [a] に、xs を xs ++ に、++ を (.) にする。

こうしておくと、[] を与えてリストに戻すときには、
(.) が右結合になってなくても ++ は右結合になる。
(((xs ++) . (ys ++)) . (zs ++)) []
= ((xs ++) . (ys ++)) (zs ++ [])
= (xs ++) (ys ++ (zs ++ []))
= xs ++ (ys ++ (zs ++ []))

実際 String の ++ を頻繁に使う class Show あたりでは、
できるだけ type ShowS = String -> String を使うことになってる。
shows :: Show a => a -> ShowS を使ってさっきの
foldl (.) id (map shows [1..10000]) []
をやってみると、今度は問題無く速い。

660 名前:デフォルトの名無しさん mailto:sage [2008/08/29(金) 17:31:29 ]
で、ReadP。

m >>= f (P の >>=)は、m の最後の return a を f a に置き換える。
それを効率よくやるには、最初っから return なんか使わないで、
Get (\c1 -> Get (\c2 -> return [c1,c2])) を
\k -> Get (\c1 -> Get (\c2 -> k [c1,c2])) みたいにしとけばいいじゃんという発想。
つまり P a を forall b. (a -> P b) -> P b に、
m を m >>= に、>>= を \m f k -> m (\a -> f a k) にする。

以下略。

661 名前:デフォルトの名無しさん mailto:sage [2008/08/29(金) 17:32:22 ]
で、余談。

foldr c n xs は、xs の : を c に、[] を n に置き換える。
それを効率よくやるには、最初っから : や [] なんか使わないで、
1:2:3:[] を \c n -> 1 `c` 2 `c` 3 `c` n みたいにしとけばいいじゃんという発想。
つまり [a] を forall b. (a -> b -> b) -> b -> b にする。
リストに戻すときは build xs = xs (:) [] を使う。
すると foldr c n (build xs) ==> xs c n と変換できる。

map f xs <==> build (\c n -> foldr (c . f) n xs)
例えばこういう変換を定義すれば、
(map f . map g) xs = map f (map g xs)
==> build (\c n -> foldr (c . f) n (build (\c n -> foldr (c . g) n xs)))
==> build (\c n -> (\c n -> foldr (c . g) n xs) (c . f) n)
==> build (\c n -> foldr (c . f . g) n xs)
==> map (f . g) xs
のように map f . map g ==> map (f . g) という変換ができる。
map f . map g 以外にも、他のいろいろなリスト関数の
foldr/build を使った形への変換を定義しておけば、いろいろな変換ができる。
foldr/build による融合変換ってやつ。今の GHC もこれを使ってる。
詳しくは GHC User's Guide の 8.13. Rewrite rules あたりを見てくれ。






[ 続きを読む ] / [ 携帯版 ]

前100 次100 最新50 [ このスレをブックマーク! 携帯に送る ] 2chのread.cgiへ
[+板 最近立ったスレ&熱いスレ一覧 : +板 最近立ったスレ/記者別一覧]( ´∀`)<225KB

read.cgi ver5.27 [feat.BBS2 +1.6] / e.0.2 (02/09/03) / eucaly.net products.
担当:undef