tr

tr コマンドもどき。文字列中の文字を置き換える。

import Data.List

tr _ _ [] = []
tr str1 str2 (c:cs) = (tr' str1 str2 c):(tr str1 str2 cs)
  where tr' str1 str2 c = case (elemIndex c str1) of
                            Just n  -> str2 !! n
                            Nothing -> c

キモは elemIndex の返り値。elemIndex は文字列 str1 中の文字 c のインデックス(0から始まる)を返す関数だけど,返り値の型が Int じゃなくて Maybe Int なので,case で場合分けをしている。はじめこの返り値の型に気づかなくて悩んでしまった。こういうのは真っ先に確認すべきだな。

*Main> :type elemIndex
elemIndex :: (Eq a) => a -> [a] -> Maybe Int

結果。

*Main> tr "ab" "AB" "banana"
"BAnAnA"

ところで,引数に文字列を3つとるのはいいけどその名前が str1,str2 っていうのは我ながらどうかと思う。こういうのってセンスが問われるよなぁ。

追記:
再帰じゃなくて map を使うようにした。ついでに引数の名前を短く。

import Data.List

tr _ _ [] = []
tr s1 s2 str = map (tr' s1 s2) str
  where tr' s1 s2 c = case (elemIndex c s1) of
                        Just n  -> s2 !! n
                        Nothing -> c

さらに追記:
tr’ の引数を省略。ちょっとすっきりした。こうやって where 節で局所定義された関数から外側の関数の引数を直接参照できるのっておもしろいねぇ。

import Data.List

tr _ _ [] = []
tr s1 s2 str = map tr' str
  where tr' c = case (elemIndex c s1) of
                  Just n  -> s2 !! n
                  Nothing -> c
*Main> tr "ab" "AB" "banana"
"BAnAnA"