定食屋おろポン

おろしポン酢と青ネギはかけ放題です

ふつうのHaskell勉強日誌

ふつうのHaskellプログラミング 初版 p246
練習問題 9.6

問題: 新しい型"Line"を定義し、sortLines関数でソートする
Line型は`data Lines = Lines {number::Int, string::String} deriving Show`で定義されるように、行番号とテキストで構成されます。
問題では、Data.Linesで定義されているsortByを使用することを推奨していますが、そうやって書いてもつまらないのでsortBy'を定義して問いてみました。
といっても、引数を関数を取るqsort関数を定義するだけですね。

module Main where

main = do 
  print $ sortLines mylines

data Line = Line {number::Int, string::String} deriving Show

mylines :: [Line]
mylines = [Line 7 "piyo", Line 5 "hogehoge", Line 4 "fugafuga", Line 3 "piyo", Line 2 "fuga", Line 1 "hoge", Line 6 "piyopiyo"]

sortLines :: [Line] -> [Line]
sortLines = sortBy' (\(Line n _) (Line m _) -> compare n m)
  where
    sortBy' :: (a -> a -> Ordering) -> [a] -> [a]
    sortBy' _ [] = []
    sortBy' f (piv:xs) = sortBy' f (filter (\x -> f x piv == LT || f x piv == EQ ) xs) ++ [piv] ++ sortBy' f (filter (\x -> f x piv == GT) xs)

出力:
[Line {number = 1, string = "hoge"},Line {number = 2, string = "fuga"},Line {number = 3, string = "piyo"},Line {number = 4, string = "fugafuga"},Line {number = 5, string = "hogehoge"},Line {number = 6, string = "piyopiyo"},Line {number = 7, string = "piyo"}]

また、これも出題にはありませんでしたが、LineをOrd型クラスのインスタンスとすればもっとスマートではないかと思って下記の実装もしてみました。

module Main where

main = do 
  print $ qsort' mylines

data Line = Line {number::Int, string::String} deriving (Show, Eq)

instance Ord Line where
  compare (Line x _) (Line x' _) = compare x x'

mylines :: [Line]
mylines = [Line 7 "piyo", Line 5 "hogehoge", Line 4 "fugafuga", Line 3 "piyo", Line 2 "fuga", Line 1 "hoge", Line 6 "piyopiyo"]

qsort' :: (Ord a) => [a] -> [a]
qsort' [] = []
qsort' (piv:xs) = qsort' (filter (<= piv) xs) ++ [piv] ++ qsort' (filter (> piv) xs)

なお、この実装2つのの問題点として、「おなじnumberを持つLineが複数あった場合、それらのソート順序は考慮されない」というものが挙げられます。
通常、同一の行番号を持つLineが複数存在するということは考えられませんが
・同一の行番号を持つLineを排除する機構は作れるのか
・同一の行番号を持つLineのリストを許容した場合、「まず行番号でソートし、同一の行番号をもつ行同士は文字列でソートする」という実装はどう行なうか
が課題といえます。

「同一の行番号を持つLineを排除する機構は作れるのか」に関して

手続き型言語脳としては、「インスタンス変数としてLineの配列を保持する新しいクラスを定義し、addLine(line)インスタンスメソッドで重複チェックを行なう。重複していればExceptionを投げる」といった方法が考えられます。
Haskellでは
・行の追加関数を用意し、その関数以外では行を追加できないようにする
・行の追加関数は成功するか分からないため、Maybeモナドを返す
という方法が良いかと思うのですが、手続き脳なのでよくわかりません。

アリンコの問題といた

職業プログラマがFizzBuzz書けない理由
タイトル: Ants
問題文: 長さL cmの竿の上をn匹のアリが毎秒1cmのスピードで歩いています。アリが竿の端に到達すると竿の下に落ちていきます。また、竿の上は狭くてすれ違えないので、二匹のアリが出会うと、それぞれ反対を向いて戻っていきます。各アリについて、現在の竿の左端からの距離xiはわかりますが、どちらの方向を向いているのかはわかりません。すべてのアリが竿から落ちるまでにかかる最小の時間と最大の時間をそれぞれ求めなさい。
制約: 1≦L≦106 1≦n≦106 0≦xi≦L
出典: プログラミングコンテストチャレンジブック(p.23) / 元はPOJ No.1852

むずかしいことはわからないけど、とりあえずHaskellでかいてみたよ。

distanceFromEdge :: Float -> Float -> Float
distanceFromEdge l x
	| l < 0.0 || x < 0.0 || x > l	= error "cant calc"
	| otherwise 			= if x < l * 0.5 then x	else l - x

ant :: Float -> [Float] -> (Float, Float)
ant l []		= (l, 0)
ant l (x:xs)	= (minFromEdge, l - minFromEdge)
	where
		minFromEdge = min (distanceFromEdge l x) (fst (ant l xs))

ant' :: Float -> [Float] -> (Float, Float)
ant' l xs = (minFromEdge, l - minFromEdge)
	where
		minFromEdge = minimum (map (distanceFromEdge l) xs)

main = do
	putStrLn (show(ant 10.0 [5.0, 2.0, 8.0, 4.0, 1.5, 3.0]))
=> (1.5, 8.5)

引数が小文字Lだと|(vertical bar)と区別つかないですね。
まだHaskellのコードを読んだことがないので、"Haskellらしさ"がわからない。
なるべくその言語らしい書き方を心掛けたいけど、わからないからフワフワしてる。

distanceFromEdge

distanceFromEdge :: Float -> Float -> Float
distanceFromEdge l x
	| l < 0.0 || x < 0.0 || l < x	= error "cant calc"
	| otherwise 			= if x < l * 0.5 then x	else l - x

インデントの作法がわからん。
ガード使う時って、"="の位置合わせたりするのかな。
if, elseは一行で書かないほうがいいのかな。

ant

ant :: Float -> [Float] -> (Float, Float)
ant l []		= (l, 0)
ant l (x:xs)	= (minFromEdge, l - minFromEdge)
	where
		minFromEdge = min (distanceFromEdge l x) (fst (ant l xs))

とりあえず再帰でも使っとけばええんやろ?ちゃうの?的な安易な発想。
whereのインデントはいくつ置けばいいの。。

ant'

ant' :: Float -> [Float] -> (Float, Float)
ant' l xs = (minFromEdge, l - minFromEdge)
	where
		minFromEdge = minimum (map (distanceFromEdge l) xs)

部分適用とか使ったら関数型っぽいんやろ?ちゃうの?的な安易な発想。

感想

インデントと改行の作法を求めて旅に出ます。

Objective-CのBlocksでカリー化関数と部分適用

すごいH本を少しずつ読み進めているんだけど、なかなか進まない。iOS6の新APIの調査の方に時間を取られています*1
5章くらいまで読んでるところだが、カリー化の説明がサラッと終わって消化不良なので、Objective-C*2で書いてみた。

multiplyThree(2)(3)(4)

で、2,3,4を引数に取り、2*3*4を返します。

typedef int (^multiplyBlk)(int);
typedef multiplyBlk (^multiplyTwoBlk)(int);
typedef multiplyTwoBlk (^multiplyThreeBlk)(int);

multiplyThreeBlk multiplyThree = ^multiplyTwoBlk(int x) {
    multiplyTwoBlk multiplyTwo = ^multiplyBlk(int y){
        multiplyBlk multiply = ^(int z){
            return x * y * z;
        };
        return multiply;
    };
    
    return multiplyTwo;
};

NSLog(@"%d", multiplyThree(2)(3)(4));
// => 24

部分適用もできます。

multiplyTwoBlk multiplyTwoBy5 = multiplyThree(5);

NSLog(@"%d", multiplyTwoBy5(6)(7));
// => 210

確かHaskellだとこんな感じ

multiplyThree :: Num a => a -> a -> a
multiplyThree x y z = x * y * z

Haskellなら2行で済むものを、わざわざObjective-Cで書く意味?ないよそんなもん。

*1:時間は作るものですよねorz

*2:正確にはCだけど

Haskellの本を買ってみた

amazon:すごいHaskellたのしく学ぼう!
とりあえずfizzbuzzを書いてみる。

-- ghci
Prelude> [if x `mod` 15 == 0 then "fizzbuzz" else if x `mod` 3 == 0 then "fizz" else if x `mod` 5 == 0 then "buzz" else show x | x <- [1..100]]
-- output
["1","2","fizz","4","buzz","fizz","7","8","fizz","buzz","11","fizz","13","14","fizzbuzz","16","17","fizz","19","buzz","fizz","22","23","fizz","buzz","26","fizz","28","29","fizzbuzz","31","32","fizz","34","buzz","fizz","37","38","fizz","buzz","41","fizz","43","44","fizzbuzz","46","47","fizz","49","buzz","fizz","52","53","fizz","buzz","56","fizz","58","59","fizzbuzz","61","62","fizz","64","buzz","fizz","67","68","fizz","buzz","71","fizz","73","74","fizzbuzz","76","77","fizz","79","buzz","fizz","82","83","fizz","buzz","86","fizz","88","89","fizzbuzz","91","92","fizz","94","buzz","fizz","97","98","fizz","buzz"]

まだ20ページくらいしか読んでないので、まだまだ手続き脳です。