Tuesday 13 August 2013

Haskell 入門! (13)

色々 predefined な関数使おうぜって話と,そういうのの調べ方.

en.wikibooks の Haskell に基づいたものです.CC-BY-SA での公開です.


h3> The need for a vocabulary
さてこうして関数の合成もできるようになり,あと再帰でもやれば色々書けるようになる,のだが,
その際には Haskell の standard libraries に何があるのかをある程度知っていることは,
コードの効率とか読みやすさ書きやすさ上結構重要になってきます.
standard libraries にある関数を自分で書き直すのももちろん勉強になるけどね.
というわけで,そのへんのこと,どう調べたらいいか,とかのお話.

Prelude and the hierarchical libraries

とうとう来ました.ghci を起動するときに出てくるアレ.
Prelude> 
この Prelude はすべての Haskell プログラムでデフォルトで読み込まれる core library.
これまで扱ったようなごく基本的な関数や,map とか filter みたいなもうちょっと高級な(?)ものなど.
そしてデフォルトでは読み込まれずに import (ghci からは :module) する必要のある module たち,
hierarchical libraries がある.
例えば permutations という関数が Data.List にあって,これを使うときはソースで
import Data.List
testPermitations = permutations "Prelude"
ghci から読み込む場合は,前にも書いた通り
Prelude> :m Data.List
複数読み込む(読み込まれる module を追加していく)時は
Prelude> :m +Data.List
+ を使う.
折角だから使ってみましょう
Prelude> :m +Data.List 
Prelude Data.List> :t permutations 
permutations :: [a] -> [[a]]
Prelude Data.List> permutations "sll"
["sll","lsl","lls","lls","lsl","sll"]
つまるところこの > の左側にあるのは現在読み込まれてるライブラリ,という感じ.

One exhibit

便利なライブラリをちゃんと使おう,な話.
関数:単語がスペースで区切られた文字列を受け取って単語を逆順に並べたのを返す.
the quick brown fox なら fox brown quick the になるもの. python なら
def rev_words(text):
    words = text.split()
    words.reverse()
    return " ".join(words)
Haskell の Prelude にはこんな関数たちがいるのだ!
  • words. (Tab とかもアリで)空白で区切られた単語を含む文字列を単語のリストにして返す
  • reverse. list を reverse する
  • unwords. words の反対.
でこれを使えば
revWords :: String -> String
revWords input = (unwords. reverse. words) input
いい感じでかけます. 一方,上の便利な関数を使わず,これまでやってきたこと + ちょっと,だけ使うならこう書ける( '''ヤバ''' い).
monsterRevWords :: String -> String
monsterRevWords input = rejoinUnreversed (divideReversed input)
    where
    divideReversed s = go1 [] s
        where
        go1 divided [] = divided
        go1 [] (c:cs)
            | testSpace c = go1 [] cs
            | otherwise   = go1 [[]] (c:cs)
        go1 (w:ws) [c]
            | testSpace c = (w:ws)
            | otherwise   = ((c:w):ws)
        go1 (w:ws) (c:c':cs)
            | testSpace c =
                if testSpace c'
                    then go1 (w:ws) (c':cs)
                    else go1 ([c']:w:ws) cs
            | otherwise =
                if testSpace c'
                    then go1 ((c:w):ws) (c':cs)
                    else go1 ((c:w):ws) (c':cs)
    testSpace c = c == ' '
    rejoinUnreversed [] = []
    rejoinUnreversed [w] = reverseList w
    rejoinUnreversed strings = go2 (' ' : reverseList newFirstWord) (otherWords)
        where
        revStrings = reverseList strings
        newFirstWord = head revStrings
        otherWords = tail revStrings
        go2 rejoined ([]:[]) = rejoined
        go2 rejoined ([]:(w':ws')) = go2 (rejoined) ((' ':w'):ws')
        go2 rejoined ((c:cs):ws) = go2 (c:rejoined) (cs:ws)
    reverseList [] = []
    reverseList w = go3 [] w
        where
        go3 rev [] = rev
        go3 rev (c:cs) = go3 (c:rev) cs
もちろん初学者たる我々にとっては読めば勉強になるだろうけれど,基本的には読む気にならないし,アレ.
そもそもこれでほんとにちゃんと動くのか確信持てないしちょっと書き換えるのも大変そう.
さらにここで
testSpace c = c == ' '
とある通り,この場合単語を区切るのは半角スペースじゃないといけない (tab や 改行コードはだめ).
など,まあこれを使いたくはない.なお Data.Char にある isSpace を使えばそのへん含めて空白か判定できる.らしい.
と,まあ色々 ''ヤバ'' いので,しっかり使える道具は使いましょう,というおはなし.

Acquiring vocabulary

こういうのを知るにはどういう手だてがあるか.

あたりがとりあえず鉄板のようだ.あとはぐぐれぐぐれ.必要なときにはまた追加を書くよ.

今日はここまで.
#企画開始時点での書き溜めがここで尽きたので,あす以降の更新はどうなるかわかりません.

No comments:

Post a Comment