「ふつうのHaskellプログラミング」読みます(6)

7章、基本的な構文。

7.1 コメント

ラインコメントとブロックコメントあり。ブロックコメントはネスト可。

Prelude> 3 + 2 -- fuga?
5
Prelude> 3 + {- aodfjaij -} 2
5
Prelude> 3 + {- aodfja {- 11111 -} -} 8
11

あとリテレイト形式といって、基本コメントのドキュメントの中にコードを埋め込む式の方法もあるらしい。D言語のhtmlの中に書くことできる機能とかもここから来てんのかね。

7.2 レイアウト

今まではオフサイドルールと言って、式の桁を揃えることでブロックを表現する方法を使っていた。

% ghc cat.hs -o cat
% ./cat <cat.hs
main = do cs <- getContents
          putStr cs

それ以外にも、中括弧{}とセミコロン;でブロックを表現することもできます。

% ghc catb.hs -o catb
%./catb <catb.hs
main = do { cs <- getContents;
  putStr cs;
  }

式区切りの基本が括弧であるように、ブロック区切りの基本は中括弧て感じ。で、すーぱーべんり記法としてのオフサイドルールがある。同じ高さの部分がブロックになるわけだから、こういうこともできる。

% ghc cat.hs -o cat
% ./cat <cat.hs
main = do
  cs <- getContents
  putStr cs

読み易くはないと思うけど、こんなに分けることもできる。

% ghc cat.hs -o cat
% ./cat <cat.hs
main = do
  cs <-
    getContents
  putStr
    cs

7.3 if文

if「文」ってのがわりと大事。制御構造じゃないのです。どういうことかっていうと、if文が値を持つということ。多くのLispとかやRubyなんかと一緒ですね。

Prelude> if 3 == 2+1 then 100 else 200
100

7.4 パターンマッチ

パターンマッチです。超重要。JavaとかC++オーバーロードとかはパターンマッチをちょっと制限して取り入れたものですよねきっと。一応Scalaで微妙に使ったことある。他の言語もなんで無いのかなと不思議に思える良い機能だと思う。良いと思うが手に馴染んでないのでもっと馴染ませる。
The Little MLerとか読んでHaskellでやるとかいいかもな。ちょっと9章のdataあたりを先取りしてLittle MLerぽい遊びをしてみよう。なんかまあ昔の自分の記録参考にしつつ。http://d.hatena.ne.jp/hogelog/20071023/p1

data ShishKebab =
  Skewer
  | Onion (ShishKebab)
  | Lamb (ShishKebab)
  | Tomato (ShishKebab)

str :: ShishKebab -> String
str (Skewer)       = "Skewer"
str (Onion  kebab) = "Onion("  ++ str kebab ++ ")"
str (Lamb   kebab) = "Lamb("   ++ str kebab ++ ")"
str (Tomato kebab) = "Tomato(" ++ str kebab ++ ")"

only_onions :: ShishKebab -> Bool
only_onions (Skewer) = True
only_onions (Onion kebab) = only_onions kebab
only_onions (Lamb kebab) = False
only_onions (Tomato kebab) = False

main = do
  putStrLn $ str (Tomato (Lamb (Onion (Lamb (Skewer)))))
  print $ only_onions (Onion (Lamb (Skewer)))
  print $ only_onions (Onion (Onion (Skewer)))

で、実行

% runhugs kebab.hs
Tomato(Lamb(Onion(Lamb(Skewer))))
False
True

うーんいい感じですね。ケバブケバブ。

あとまあ普通に本に乗ってる感じのを試すと、こんなんとか。

Prelude> let p (x,y) = x + y
Prelude> map p $ zip [1,2,3] [4,5,6]
[5,7,9]

ガードとか。

hoge x y
  | x == y = "eq"
  | x > y = "gt"
  | x < y = "lt"

main = do
  putStrLn $ hoge 2 8
  putStrLn $ hoge 2 2
  putStrLn $ hoge 8 2
% runghc test_guard.hs
lt
eq
gt

でもまあこの辺は使ううちに慣れてくるだろうからまあいいや。

7.5 case式

パターンマッチを関数定義以外のところで使う方法。先のパターンマッチがOCamlで言うfunctionなら、こっちはmatchみたいなものですね。

Prelude> import Char
Prelude Char> case "hoge" of {"" -> ""; (c:cs) -> toUpper c : cs}
"Hoge"
Prelude Char> case "" of {"" -> ""; (c:cs) -> toUpper c : cs}
""

7.6 関数定義

関数の識別子として使えるのは[a-zA-Z_][a-zA-Z_0-9']*。二項演算子

Prelude> let (///) x y = x / y / y
Prelude> 27 /// 3
3.0

のように、括弧でくくることで定義できる。優先順位云々はめんどいのでパス。

7.7 定義と束縛

let式

たびたび使ってましたが。一時的に束縛できる。schemeだとかMLだとかと似たようなもんと認識。

f n = let x = n + 1
          y = x + 1
          z = y + 1
      in x * y * z

main = print $ f 2

うーん、オフサイドルールがわりとめんどいなあ

% runghc test_let.hs
60


次where節による束縛

foo i | i > 0 = square (i + 1)
      | i == 0 = square 0
      | i < 0 = square (i - 1)
  where square n = n * n

main = do
  print $ foo 1
  print $ foo 0
  print $ foo $ - 1
% runghc test_where.hs
4
0
4


束縛においてもパターンを用いることが可能。

Prelude> let (c:cs) = "hogehoge"
Prelude> c
'h'
Prelude> cs
"ogehoge"

7.9 練習問題

こんなんかなあ。きたねえ気してならん。

main = do cs <- getContents
          putStr $ fold cs

fold :: String -> String
fold cs = foldIn $ splitAt 60 cs

foldIn :: (String,String) -> String
foldIn (line,[]) = line ++ "\n"
foldIn (line,remain) = line ++ "\n" ++ fold remain
% ghc fold.hs -o fold
% cat hoge
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
% ./fold <hoge
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccc
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
cccccccccccccccccccc

いちおうそれっぽい。あとはねむいからねる。

くそ間違ってた。

ねむいからといってそんな、あきらかに間違ってるのどうよ。

main = do cs <- getContents
          putStr $ concatMap fold $ lines cs

fold :: String -> String
fold cs = foldIn $ splitAt 60 cs

foldIn :: (String,String) -> String
foldIn (line,[]) = line ++ "\n"
foldIn (line,remain) = line ++ "\n" ++ fold remain
% ghc fold.hs -o fold
% cat hoge
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
% ./fold <hoge
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
cccccccccccccccccccccccccccccccc

これでよろし。

test