bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

Haskell ドリル17 map関数(解答)

問題1:自然数のリスト[1,2..]より各要素を5だけ増やしたリストを作成する。即ち、[6,7..]を作成する。

Prelude> take 10 $ map (+5) [1,2..]
[6,7,8,9,10,11,12,13,14,15]

問題2:自然数のリスト[1,2..]より各要素を二乗したリストを作成する。

Prelude> take 10 $ map (\x -> x * x) [1,2..]
[1,4,9,16,25,36,49,64,81,100]

問題3:自然数のリスト[1,2..]より3の倍数のリストを作成する。

Prelude> take 10 $ map (*3) [1,2..]
[3,6,9,12,15,18,21,24,27,30]

問題4:アルファベットだけからなる文字列を得て、各文字を一つ後の文字に変更した文字列を出力する。但し、z,Zの後の文字は、それぞれa,Aとする。
答:Haskellでは、ifによって場合分けする時は、if .. then .. elseを用いる。

Prelude> map (\x -> if x == 'z' then 'a' else if x == 'Z' then 'Z' else succ x) "AdeTEfzyjZawrTKI"
"BefUFgazkZbxsULJ"

問題5:リストが与えられた時、各要素に対して、自分とそれに続く要素を対にしたリストを作成する。例えば、[1,2..]は、[(1,2),(2,3),(3,4)…]となる。
答:無限のリストを前提にする(有限の場合には、最後の対を求める時に特別な処理が必要)。次のプログラムはトリッキーである。無限のリストlstを受け取り、これから、これを最初の要素とし、さらに、これから最初の要素を除いたリストを二番目のリストとし、同じように、最初の要素を取り除いたリストを次の要素とするリストlistofLists \ lstを作成する。このように、無限リストの無限リストを作成するようなことはHaskell以外の言語では難しい。

Prelude> let listOfLists lst = lst : listOfLists (tail lst)
Prelude> take 10 $ map (\x -> (head x, head $ tail x)) (listOfLists [1,2..])
[(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10),(10,11)]

問題6:数のリストが与えられた時、各要素に対して、自分とそれに続く要素とを足したリストを作成する。

Prelude> let listOfLists lst = lst : listOfLists (tail lst)
Prelude> take 10 $ map (\x -> (head x + (head $ tail x))) (listOfLists [1,2..])
[3,5,7,9,11,13,15,17,19,21]

問題7:リストが与えられた時、大文字を小文字に変更したリストを作成する。
答:関数toSmallを用意する。

toSmall :: Char -> Char
toSmall a 
  | a `elem` capital = getSmall' a pair
  | otherwise = a 
  where
    small = ['a'..'z']
    capital = ['A'..'Z']
    pair = zip small capital
    getSmall' :: Char -> [(Char, Char)] -> Char
    getSmall' _ [] = error "A Small letter does not exist."
    getSmall' a' ((ySmall, yCapital):ys)
      | a' == yCapital = ySmall
      | otherwise = getSmall' a' ys

これを用いると次のようになる。

*Main> map toSmall "TOKYO is popular in the WORLD."
"tokyo is popular in the world."

問題8:文字列が与えられた時、それぞれの文字を小文字、大文字を区別しないで、アルファベットの順番、例えば、a,Aは1、h,Hは8、に変換したリストを作成する。
答:関数toOrderを用意する。

toOrder :: Char -> Int
toOrder a 
  |a `elem` small = getNumber a small
  |a `elem` capital = getNumber a capital
  | otherwise = error "It is not an alphabet."
  where
    small = ['a'..'z']
    capital = ['A'..'Z']
    getNumber :: Char -> String -> Int
    getNumber a' lst = getNumber' a' lst 0
    getNumber' :: Char -> String -> Int -> Int     
    getNumber' _ [] _ = error "It is not included in the list."
    getNumber' a1 (x:xs) n 
      | a1 == x = (+1) n
      | otherwise = getNumber' a1 xs ((+1) n)

これを用いると次のようになる。

*Main> map toOrder "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,26,25,26,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]

問題9:数字のリストが与えられた時、奇数であれば1に偶数であれば0に変換したリストを作成する。

Prelude> map (\x -> if even x then 0 else 1) [3,7,2,6,8,14,63,45]
[1,1,0,0,0,0,1,1]

問題10:数字のリストが与えられた時、それぞれを3で割った余りに変換したリストを作成する。

Prelude> map (\x -> mod x 3) [3,7,2,6,8,14,63,45]
[0,1,2,0,2,2,0,0]