またHaskell基本
Haskellの基本動作あれこれを少し。ループはアキュムレータを再帰で渡して実現。しかし、プライムで別の値を作るのって効率悪くないのかしら。以下の例は10進数の数値を表す文字列を数値に変換する。
import Data.Char asInt :: String -> Int asInt xs = loop 0 xs loop :: Int -> String -> Int loop acc [] = acc loop acc (x:xs) = let acc' = acc * 10 + digitToInt x in loop acc' xs
% ghci GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help Loading package base ... linking ... done. Prelude> :load loop.hs [1 of 1] Compiling Main ( loop.hs, interpreted ) Ok, modules loaded: Main. *Main> asInt "3376" 3376 *Main> asInt "1234" 1234
zipWithとかも面白い。Haskellでひとこぶラクダ的なキャメルケースを使う理由って、dropWhile、takeWhileのように合成関数のような感じのことをよくやるからなのかしら。
Prelude> zipWith (*) [1..10] [2..11] [2,6,12,20,30,42,56,72,90,110] Prelude> drop drop dropWhile Prelude> dropWhile even [1,2,3,4,5] [1,2,3,4,5] Prelude> dropWhile even [2,3,4,5] [3,4,5] Prelude> dropWhile even [2,4,5,6,8] [5,6,8] Prelude> takeWhile even [2,4,5,6,8] [2,4]
Prelude> zipWith (*) [1..10] [2..11] [2,6,12,20,30,42,56,72,90,110]
というのは、Rubyでは、
>> (1..10).zip(2..11).map{|a|a.inject(:*)} => [2, 6, 12, 20, 30, 42, 56, 72, 90, 110]
という感じかしら。なるほど。
一瞬、(1..10).zipは(1..10).to_a.zipとすべきなのだろうかと迷ったけど、zipというのはRangeやArrayのメソッドではなくて、Enumerable#zipでArrayやRangeにmix-inされてるのだった。なるほど! このへん、Rubyってよくできてるなぁと思う。そうか、そう考えると、mix-inとして実装すべきなんだから「Rangeでもzipできるはず」という直感は働くのかも。まあ、そんなの覚えればいいだけかもしれないというのもあるけど。
一方、Rubyのようにオブジェクト中心だと、関数をいっぱいつなげるような処理では、どうも不自然な流れになりがちな気がする。mapの中でレシーバを明示しないといけないとか、zipが作用する2要素が表記上ではレシーバと引数というように等価でないとか。このzipWidthの例だと、Haskellのほうが美しいように思う。