アルゴ式 「文字コード」をHaskellで解いて、その復習
Haskellは使っている人が少ないので、初学者でも把握できて見本に出来るようなコードを見れる機会が少ないと思います。そこで、初学者向けの問題と解説を行っている「アルゴ式」のサイトの問題を実際に解いたコードと、その解き方の復習を記しておきたいと思いました。
つまりは、初学者による初学者のためのHaskellコード 集がここに爆誕!していきます。
問題
アルゴ式の「コーディングによる問題解決」にある「ロジック実装中級」の「文字と文字列」の中の問題「文字列コード」を解いたときの復習ノートです。
問題「文字コード」は以下のページ。
この問題の中心は、文字コードと文字の相互変換を行う処理です。
ord
とchr
文字と文字コードは文字と文字コードの行き来は、ord関数とchr関数を使うのが定番です。
関数 | 処理 |
---|---|
chr | 文字コードの数値を文字に |
ord | 文字を文字コードの数値に |
この関数はData.Char
モジュールをインポートする必要があります。
ghciなら:m
を使って読み込みます。
ghci> :m Data.Char
文字コードを文字にするならchr
。なので、'a'に対応する数値97を渡して実験してみましょう。
print $ chr 97
文字から文字のコードにしたい場合は、ord
(ordは、orderつまり順序の略)。
そこで、ord に直接'a'を渡してコードを確認してみましょう。
print $ ord 'a'
使いどころ
シーザー暗号系の文字コードをいくつかずらす系の場合、
この関数で文字を数値に変換することで数値的に処理できます。
解答例
問題では、出力するフォーマットが指定されています。
なので、数値を受け取ってshow
やchr
を組み合わせて文字列にする関数を定義し、これをmap系で数値リスト(指定の97から122)を関数に一個づつ渡す流れが良さそうです。
解答例
import Data.Char
main :: IO ()
main = do
mapM_ (putStrLn . solve) [97..122]
solve :: Int -> String
solve i = show i ++ " " ++ [chr i]
この解答例を組み立てるために必要なhaskell基礎知識は以下の通りです。
[]
とhead
文字と1文字の文字列の相互変換ならhaskellでの「文字列」は「文字のリスト」ということを把握するのは必須。
文字列はリストなので++
演算子で結合できます。また、そもそも、文字列は、出力の際にputStrLn
で出力しますが、これに文字を混在できないので、文字を文字列へ変換する処理が必要があります。
文字を1文字の文字列にするのは[]
でくくってリストにすれば良いです。
print ['a']
逆に1文字の文字列を文字にするには、head
で一番はじめの要素を取れば良いです。
print $ head "a"
この文字列から文字への処理は、更に、ordを使って文字コードにする処理の場合に活用できます。
show
とread
数値と文字列の相互変換ならhaskellで数値とその数値の文字列の相互変換はshowとreadを使うのが定番。
これらが対になっていることを把握しておきます。
関数 | 処理 |
---|---|
show | 数値を文字列に |
read | 文字列を数値に |
本来的には文字通り、show
は、内容を表示するために文字列化するもので、
read
はデータ内容を表現した文字列をそのデータの値として読むものです。
なので、特にread
を利用する場合、原則として、値の型を指定する必要があることを意識しておきましょう。
ghciで以下を試すと、出力が"で囲われて文字列であることがわかります。
print $ show 1
一方、ghciで以下を試すとパース(読めない)とエラーが出ます。
これは、"1"を何の値として読めばよいかがわからないからです。
print $ read "1"
なので、read
がすべきことを丁寧(型シグネチャ)に書いてあげれば、ちゃんと読み込めます。
例えば、整数(Int)なら
print $ (read :: String -> Int) "1"
じゃなくて、実数(Double)なら
print $ (read :: String -> Double) "1"
mapM_
リストを改行区切りで出力するならmapM_
関数の1つ目の引数に標準出力へ表示させるIOアクションです。
よくあるパターンは、putStrLn
かprint
をおいて、その後にリストを置きます。
mapM_
の最後のアンダーバー'_'を忘れないように!
数値等ならprint
mapM_ print [1..10]
文字列ならば、putStrLn
mapM_ putStrLn ["osaka","kyoto","nara"]
リスト処理の考察と関数の引数の把握
mapM_
の1つ目の引数には(a->IO())
のような関数、つまり、ある値をとってIOアクションを返すような関数がきて、2つ目の引数がリストになります。
上の例では、第1引数の関数自体合成関数にして、数値を受け取っています。
mapM_ (putStrLn . solve) [97..122]
これを、第一引数の関数をputStrLn
にして、文字列を受け取るならば、次のようになります。
mapM_ putStrLn $ solve [97..122]
haskellでは、各ワードの結合の強さを意識、特に、演算子がないもワードの羅列がもっとも結合が強いことに意識して、関数の引数がどうなっているかを把握するのに慣れる訓練が必要です。
Discussion