少しは関数言語っぽくかけたかな?
昨日からHaskellで簡単な問題を解いていた結果、謎のエラーはあまりでなくなりましたが、やっぱり強烈に厳格な型があるのでその絡みで詰まることがあります。
さて、この前問題を解いたときはいまいちな仕上がりでしたが、今回はいくぶん関数言語らしく書けた気がします。解いた問題はこちら。
高橋君はアメリカに留学しようと考えており、成績表を提出することになりました。
アメリカ留学の成績表には、学力を測る指標であるGPAを表記する必要があります。
GPAとは各単位に対する評価(A,B,C,D,F)を点数に換算して平均した値で、点数への換算は以下のようになります。
A評価 → 4 点
B評価 → 3 点
C評価 → 2 点
D評価 → 1 点
F評価 → 0 点
全てF評価だった場合は、GPAは 0 になります。
高橋君の各単位に対する評価をもとにGPAを求めなさい。
そして無事に正答したコードがこちら。
import Data.List getGPA :: Char -> Int getGPA rank | rank == 'A' = 4 | rank == 'B' = 3 | rank == 'C' = 2 | rank == 'D' = 1 | rank == 'F' = 0 answer i = (realToFrac (sum (map getGPA i))) / (genericLength i) main = do getLine getLine >>= putStrLn.show.answer
整数を割ることができないのは衝撃でした。必ず実数に変換してから割る必要があるみたいです。
以前と比べて目新しいところはガードと関数合成でしょうか。
getGPAのあたりで一直線に|が並んでいるのがガードです。上から「=」の左辺を評価して、該当するものがあれば「=」の右辺を返します。Cのswitch文に近いものですね。今回は入力が限られているので書いてませんが、いずれにも該当しない場合は「otherwise」を左辺にするとelseと同じような感じになります。
引数がひとつだけの関数f,gがあったとき、f.g x
とすると関数を合成します。数学でいうところのっぽい感じですね。もちろんg(x)の返り値の型はf(x)の引数の型と同じでないといけません。ちなみに以下の3行の実行結果は同じです。
f (g x) f $ g x f.g x
真ん中のやつですが、$は最低の優先度もつ演算子で、うまく使うとLISPのようなかっこ地獄から解放されます。本来の効用は…なんでしょうかね…