Programing in Haskell / Chapter.9 Excsise(2)

lifeゲームで画面がチカチカしないようにする。最近の更新頻度がやばい

-- タプルを受けっとって、lifeゲーム開始
life                          :: (Board , Board) -> IO ()
life (alive,die)              =  do { showcells alive die
                                     ; wait 5000
                                     ; life (nextgen alive)
                                     }

-- 生きているのはO,死んでるのは空白を出力する
showcells                     :: Board -> Board -> IO ()
showcells alive die           =  do { seqn [writeat p " " | p <- die]
                                    ; seqn [writeat p "O" | p <- alive]
                                    }


-- 死んだposのリストを返す
dies                          :: Board -> [Pos]
dies b                        = [p | p <- b, elem (liveneighbs b p) [1,4]]

Programing in Haskell / Chapter.9 Excsise(1)

一応、実装してみたものの。なんか違いが全然わからない…
今日は、なんだからダラダラしちゃった。ちょっと工夫して続きやろう。

> readLine :: IO String
> readLine = readLineInteractive []
>          where readLineInteractive xs =
>          do { c <- getChar
>             ; case (c, xs) of
>                    ('\n'  , xs)   -> return (reverse xs)
>                    ('\DEL', x:xs) -> do { putStr "\ESC[1D"
>                                           ; readLineInteractive xs
>                                         }
>                    otherwise      -> readLineInteractive (c:xs)
>             }
> getLine' :: IO String
> getLine' =  do { x <- getChar
>                ; if x == '\n' then return []
>                  else do { xs <- getLine
>                          ; return (x:xs)
>                          }
>                }

Programing in Haskell / Chapter.9 Interactive Programs

http://www.amazon.co.jp/dp/0521692695 p.88-94
電卓を作るというところ。前の章で(>>=)を粘って考えたおかげで、今日の部分はかなりすっきり読めた。windowsでやってたんだけど、急遽linuxにPCを切り替えて、作業をすることに。
おもしろい定義がしてあるところがあって、Worldという型をつかってハードウェアの状態を説明する。

type IO = World -> World
type IO a = World -> (a, World)

実際には、Curryを活用して、ハードウェアの状態を隠蔽するんだそうな。

-- type constructor
newtype IO a

-- constructors:
IO :: ((a -> IOResult) -> IOResult) -> IO a

-- instances:
instance Functor IO
instance Monad IO

ハードウェアの状態を記述するから、副作用とは無縁といえる?いえない?ちょっと曖昧です。
面白かったのはここ

IO ()

IO () is the type of actions that return the empty tuple as a dummy result value. Actions of latter type cat be thought of as purely side-effecting actions that return no result value ....

純粋に副作用のある命令ってw確かに、副作用があって、他にも値が返ってくると、わかりにくくなるものなのかなぁ。C言語の文字列取得とか、ポインタ渡しつつ返り値でエラーチェックとか考えるとたしかに一理あるのかもしれないですね!
またPHP Real Worldの翻訳するので今日はこのへんで!

Programing in Haskell / Chapter.8 Exercise (2)

Programing in Haskell(http://www.amazon.co.jp/dp/0521692695) P.85~86

Chapter8は、いつか読み直す。それなりに理解できたからOK
指数計算の部分は、名前をつける部分を見ようとしたら、カンニングしてしまった。。でも、そんなに難しくないと思われ

expr :: Parser Int
expr  =  do t <- term 
            do symbol "+"
               e <- expr
               return ( t + e )
               +++ do symbol "-"
                      e <- expr
                      return ( t - e )
               +++ return t

term :: Parser Int
term =  do f <- factor
           do symbol "*"
              t <- term
              return (f * t)
              +++ do symbol "/"
                     t <- term
                     return ( f `div` t )
              +++ return f

factor :: Parser Int
factor  =  do a <- atom
              do symbol "^"
                 f <- factor
                 return ( a ^ f )
              +++ return a

atom   :: Parser Int
atom    =  do symbol "("
              e <- expr
              symbol ")"
              return e
              +++ natural

eval     :: String -> Int
eval xs   = case parse expr xs of
            [( n, [] )] -> n
            [(_, out )] -> error ("unused input " ++ out)
            [] -> error "invalid input"

Programing in Haskell / 8.10 Exercises(1)

Programing in Haskell(http://www.amazon.co.jp/dp/0521692695) P.85
問題1.2を解いた。パーサーについて勉強するのは、初めてなので、勉強になりますね!難しいところだと思うので、あせらずゆっくり解いていきます。
例によってソースコードは、サンプルコードを拡張した形になります。
どうやったらいいのか、まったくわからなかったので、とりあえず(-num)の形をパース刷る関数を作成して、もともとのnatと合成する感じです。

-- int :: Parser を定義せよ
-- 自分の答え. ちょっと冗長だったか。
minus                         :: Parser Int
minus                         =  do x <- char '-'
			      n <- many1 digit
			      return ((-1) * (read n))

int'                          :: Parser Int
int'                          = do x <- (nat +++ minus)
                                   return x
-- 本の答え
int                           :: Parser Int
int                           =  do char '-'
                                     n <- nat
                                     return (-n)
                                 +++ nat

お次は、コメントの定義。大御所のdoの書き方を真似てやってみました。これも結構時間がかかった。素直に書けばよかった!

-- comment::Parserを定義せよ
comment                       :: Parser ()
comment                       = do { symbol "--"
                                   ; many (sat (/= '\n'))
                                   ; item
                                   ; return ()
                                   }

コメントが定義できたときは、気持ちよかった!こういう抽象度の高いものをすぐ実装できたら、すごい気持ちいいだろうなぁ

Programing in Haskell / Chapter.8.4 Sequencing

Programing in Haskell(http://www.amazon.co.jp/dp/0521692695) P.74-77
どうしても、(>>=)で、パースしていくところがわからなかったので、細かくしらべた。
すごい参考になりました。ありがとうございます。
http://hippos-lab.com/blog/node/353
http://hippos-lab.com/blog/trackback/353(TrackBack)

こんなケースを考える。(>>=の定義はサンプルコードを探すとわかる)

parse item >>= (\x -> item >>= (\_ -> item >>= (\y -> return (x,y)))) "abcd"

とりあえずこんな風に置き換える.

item >>= (\x -> item >>= (\_ -> item >>= (\y -> return (x,y)))) "abcd"
item >>= f(x) "abc"

この式の展開は以下のとおり

item >>= f(x) "abcd"
  <-> case parse item "abcd" of
                 [] -> []
                 (['a', "bcd"]) -> parse f ('a') "bcd"

f(x)の内容を展開する

f('a')  <->
item >>= g(_) "bcd"
  <-> case parse item ??? of 
                 [] -> [] 
		 [('b', "cd")]  -> parse g ('b') "cd"
                 -- 式の結果からこうなっているはずです!
  • (parse item 'a')になったらエラーじゃないか!となっていたのですが、

定義ではf(x)に'a'が入っていただけで、それが(parse item inp)に適用されるとは書いていません。ここでで帰ってくるのは計算結果ではなく、手続き(クロージャ)が帰ってくるのですね!この結果、下は、inpの引数を求めて、手続きを返します。その隣にちょうどよく"bcd"が配置されているので、(\inp -> ...)が無事関数として実行されます。

(\inp -> ...) -- -> 手続きが帰ってくる
-- こう解釈される
(\inp -> ...) "bcd"

こうなれば、すっきりして解釈を進めることができますね!全体がどう解釈されていくのかを、haskellの文法ではありませんが書いてみました。

item >>= f(x) "abcd"
  <-> case parse item "abc" of
                 [] -> []
                 (['a', "bc"]) -> parse f ('a') "bc"
    
item >>= g(_) "bcd" -- \inpに"bcd"が適用
  <-> case parse item of -- 手続きを返します 
                 [] -> []
		 [('b', "c")]  -> parse g ('b') "c"

item >>= h(y) "cd"
  <-> case parse item "c" of
                 []  -> []
		 [('c', "")]   -> return ('a', 'c')

引数に突っ込んでいた'a'は、returnの時に返されることになります。returnも引数を二つとるので、値そのものを返すのではなく、手続きを返すことになります。

(\inp -> ... return (x, y) ) "d"
-- -> ([('a', 'c') , 'd'])

うーむ。奥が深いですね。明日は、残りと演習問題いってみよう!ちょっとここら辺から自分の未開拓領域に入っていくので、ペースが遅くなりそうです。でも、わかったときはいいね!