1 Getting Starged 使ってみよう ぱらぱらめくる『The Haskell Road to Logic, Maths and Programming』
- この本は、各章がモジュールになっている
- hugsを使うことを前提として書いてある(このブログはghciを使っている。ずっと続けられるか、どこかでうまくいかなくなるかは、おいおいわかるはず)
- Exercise 1.3
- 以下をprime.hsというファイルで保存
divides d n = rem n d == 0
-
- 保存したディレクトリで以下、実行してghciを立ち上げ
$ ghci
-
- ghci内で色々やってみる
GS> :l prime.hs [1 of 1] Compiling Main ( prime.hs, interpreted ) Ok, modules loaded: Main. Main> divides <interactive>:63:1: No instance for (Show (a0 -> a0 -> Bool)) arising from a use of ‘print’ In a stmt of an interactive GHCi command: print it Main> :t divides divides :: Integral a => a -> a -> Bool Main> :i divides divides :: Integral a => a -> a -> Bool -- Defined at p Main> :k divides <interactive>:1:1: Not in scope: type variable ‘divides’ Main> divides 3 5 False Main> divides 3 6 True Main> divides 6 3 False Main> :r Ok, modules loaded: Main.
- 素数の判定アルゴリズム(素数判定)。ある自然数nについてまでで割り切れなければ、nは素数、というのを実装しているのが以下
- remは剰余を返す関数、dividesはそれを使って割り切れるかどうかを判定する関数、ldfは再帰的にまで試す関数で、ためし割りをする数が大きくなるまで続けはTRUEを返し、そうでなければFALSEを返す。ldはそれの『入口』の関数。それらを使って判定する関数がprime0
module GS -- Getting Starged moduleと言う意味 where -- "where"に続く諸々を条件としたモジュールですということ {- {- 関数の定義は型に関する部分と、実行内容に関する部分からなる-} {- 型定義には"::"が使われる -} {- 型定義は省略されることもある-} {- 実行内容記述では、"="の左側"divides d n"が関数名と引数 -} {- 右側"rem n d == 0"が引数を使って実行する内容 -} {- 今回の場合は、"rem n d"という関数に引数n dを渡した結果 -} {- と0との一致を"==""で確認した結果を返す-} {- という内容 -} -} divides :: Integer -> Integer -> Bool divides d n = rem n d == 0 -- d is divisible by n {- {- Least divisor -} {- 1より大きい整数でnを割り切る最小のものを返す -} {- この値が引数自身であれば、それは素数 -} -} ld :: Integer -> Integer ld n = ldf 2 n ldf :: Integer -> Integer -> Integer ldf k n | divides k n = k | k^2 > n = n | otherwise = ldf (k+1) n -- 再帰的なldfの利用 prime0 :: Integer -> Bool prime0 n | n < 1 = error "not a positive integer" | n == 1 = False | otherwise = ld n == n
- ldは1より大きい最小の約数。map関数を使って、1,...,10の最小約数を確認
GS> map ld [1..10] [1,2,3,2,5,2,7,2,3,2]
GS> prime0 $ 2^(2^1)+1 True GS> prime0 $ 2^(2^2)+1 True GS> prime0 $ 2^(2^3)+1 True GS> prime0 $ 2^(2^4)+1 True GS> prime0 $ 2^(2^5)+1 False GS> prime0 $ 2^(2^6)+1 False
- Exercise 1.4
- ldfの">"記号を">="記号に換える。ldfの下流の処理、ld,prime0も換える
ldf' :: Integer -> Integer -> Integer ldf' k n | divides k n = k | k^2 >= n = n -- > を >= に換えた | otherwise = ldf' (k+1) n -- 再帰的なldfの利用 -- ldf'対応に換えた ld' :: Integer -> Integer ld' n = ldf' 2 n prime0' :: Integer -> Bool prime0' n | n < 1 = error "not a positive integer" | n == 1 = False | otherwise = ld' n == n
-
- 結果は、
map prime0 [1..20] map prime0' [1..20]
-
- と変わらない。処理の違いは、k^2 > n と k^2 >= nとの違いであって、それはk^2==nの場合の違いであるが、そのような場合、nはkで割り切れているから、k^2 >(=) nの判定よりも先にdivides k n結果がTRUEとなりその結果が採用されるから
- Exercise 1.5
- In the following codes.
- Exercise 1.6, 1.7
- divides 自体は、Integerをとって、次にIntegerをとって、Boolを返すもの
- divides 3 (divides に3を引数として与えた状態)は、もう一つIntegerを取ってBoolを返すもの
- divides 3 6 (divides に第一引数として3を、第二引数として6を与えた状態)は、Bool値(を持った状態)である
GS> :t divides divides :: Integer -> Integer -> Bool GS> :t divides 3 divides 3 :: Integer -> Bool GS> :t divides 3 6 divides 3 6 :: Bool
- Identifiers
- 2種類
- Variable identifiers
- 小文字始まり(map,max,fct2list,fctToList,fct_to_list,max')
- Constructor identifiers
- タイプを定める。大文字始まり(Bool,Int)
- 関数はdata-structuresに働きかけるもの、コンストラクタはdata-structureを作り上げるパーツ・ブロック単位
- 予約語
- case, class, data, default,deriving, do,else,if,import,in,infix,infixl,infixr,instance,let,module,newtype,of,then,type,where,_ (<-underscore)
- Exercise 1.9 最小・最大用関数。リストをとって値を返すか、2つの値をとって1つの値を返すか
myMimIntList :: [Int] -> Int myMimIntList [] = error "empty list" myMimIntList [x] = x myMimIntList (x:xs) = min x (myMimIntList xs) myMimIntTwo :: Int -> Int -> Int myMimIntTwo x y | x <= y = x | otherwise = y myMaxIntList :: [Int] -> Int myMaxIntList [] = error "empty list" myMaxIntList [x] = x myMaxIntList (x:xs) = max x (myMaxIntList xs) myMaxIntTwo :: Int -> Int -> Int myMaxIntTwo x y | x >= y = x | otherwise = y
- Exercise 1.10
- リストの先頭の値が、指定の値だったら、それを除いたリストを返す(返すリストはカラリストかもしれない。ただし空のリストの先頭はないので、引数のリストが空なら、帰り値は空リストであると指定する。先頭の値が指定の値でなければ、先頭の値と、「残りのリストについて再帰的に値チェックをした出力」を連結したリストを返す
- 「作業」は値とリストを受け取ってリストを返すことであるし、先頭を取り除く作業と、取り除かれない値は、残りとくっつける作業だけがわかれば、できる作業であるので、その2作業だけが書ければ、後は、実行できるはず、という感じの作り
- myRemoveFstとして作った関数を、別途、関数内で使うに当たり、条件の与え方としてwhereを使う方法とletを使う方法があるので、それに対応して2つの関数を書いておく
myRemoveFst :: Int -> [Int] -> [Int] myRemoveFst x [] = [] myRemoveFst x (y:ys) | x==y = ys | otherwise = y: ( myRemoveFst x ys) mySortIntList :: [Int] -> [Int] mySortIntList [] = [] mySortIntList xs = m : (mySortIntList (myRemoveFst m xs)) where m = myMimIntList xs -- 別の書き方、where を使うか let を使うか mySortIntList' :: [Int] -> [Int] mySortIntList' [] = [] mySortIntList' xs = let m = myMimIntList xs in m : (mySortIntList' (myRemoveFst m xs)) where
- 整数だけを扱ってきたが、平均を計算したりすると、そうもいかない
- 数値関係の諸型についてはこちらを
-- Intからの変換は何でもfromIntegral -- Floatへ変換するのは、型定義でFloatになるように指定しているから myAverage :: [Int] -> Float myAverage [] = error "empty list" myAverage xs = fromIntegral (sum xs) / fromIntegral (length xs) -- Rational型は「整数を分母分子に持つ」と言う型 -- Intは分母が1のRational型の一部とも言える myAverage' :: [Int] -> Rational myAverage' [] = error "empty list" myAverage' xs = fromIntegral (sum xs) / fromIntegral (length xs)
-
- 二通りの関数の出力を比較
|