その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

スタートHaskell2 #2 に参加してきました

納期: 7日遅れ

第2回目スタートHaskell2 に参加してきました。
そう、それは1週間前の話です。可成り遅れたレポートと相成りました。

聞いてきた事


くわしく

  • 第3章
    • xs ++ ys は「パターン」ではなく「関数」。従ってパターンマッチできない。
      • 「パターン」はHaskell により定義されている。それによって定義された物以外はパターンマッチの対象外。
    • '_' から変数名を始めても良い。
      • 例えば、"_garbage" や"_foo" など
      • '_' を単体で使った時と同様に、その中身は捨てられてしまう。
      • 明示的に、「この値はこうした理由で使われませんよ」と表したい場合に使う。
      • とは言ったものの、あまり使われないらしいです。
    • otherwise は変数であり、True と定義されている。
      • Bool 値だから、ガードの判定式として扱える。True なので常に引っかかる。
    • where 節に関数を書く場合は、型定義はあまり書かない。
      • なぜか: 大体の場合、型推論が効くから。
      • でも、where の中に型シグネチャを書くことも出来る。
      • where の直後に(同一行に)where の内容を書くかどうかは好みの問題。でも、改行した方が良いんじゃないですか?
    • let 式はその名の通り「式」
    • let とlet in の違いとは
      • リスト内包表記の中にlet を記述する場合はin は必要無い。(シンタックスシュガー)
      • GHCi を起動するとIO モナドのdo の中にいる事になる。つまりリスト内包表記の中にいるのと同義。
        • だからGHCi の中ではlet にin を付けなくても書ける!
      • 式と式の関係性がない、つまり列挙しているようなスタイルの場合はin を書かなくても良い、という認識。
      • でも、初心者は「let が来たらin が来る」という認識で良いんじゃないの、とのこと。
    • let よりもwhere で書いた方が良い。その方が宣言的だから。
    • case 式はその名の通り「式」
      • case 式は書かない方が良い。関数のトップレベルで分岐させるべき。
    • 実は、if はcase のシンタックスシュガー
    • 分岐について詳しくは -> Haskellの文法(分岐編) - あどけない話

  • 第4章
    • Haskell に限った話ではないが、二分木が爆発的に増えるようなアルゴリズムは遅い
      • 例えばそれは、単純な(愚直な)フィボナッチ数を求めるアルゴリズム
      • はやいフィボナッチ -> Fast fib
    • Haskell の関数はCall してReturn する訳じゃない
      • 手続き型言語なんかは、関数をCall スタックに積まれるけど、Haskell の場合はJump する。
      • むしろ、スタックオーバーフロー云々よりも桁あふれが危なかったり。
    • '!!' 演算子の仕様: 第2引数がInteger だと死ぬ。第2引数はInt 限定。

  • 第5章
    • foldl とfoldr はどちらを使うべきか
      • リストを引数にとって、リストを結果として返すときはfoldr を使うべき
      • 数値に畳み込みを行うような時はfoldl' を使うと良い
        • 遅延評価しないから、良く分からない計算が膨れあがらない。無駄にヒープ領域を使わない。
      • 基本的にfoldl は要らない子。foldr とfoldl' があれば大体何とかなる。
    • 式に括弧が沢山付いている場合の処理
      • 一番外側の括弧から先に外しましょう。
        • 一番右の閉じ丸括弧を消して、それに対応する開き丸括弧を$ に置換する。それを順に行っていく。
        • 一番右の$ 以外は. に置換することが出来るので置換する。
        • 見やすい!!!!
        • これを使って、かつポイントフリースタイルを用いると、関数合成によって関数を書ける(!)
        • 詳しくはこちら 関数合成の妙技 - あどけない話

  • その他
    • そのプログラムがメモ化されているかどうかは分からない。Haskell の欠点でもある。
      • メモ化されているかどうかは、値が束縛されている変数を再利用しているかどうかが指標となる
      • 慣れればいずれ見えるようになる、はず
      • メモ化について ->
    • Haskell ではTab を使わないのがマナー。ソースコードに\t が入っているのは好ましくない。
    • 関数が失敗する可能性があるのであれば、Maybe を使うべき。
      • あと、良い方法とは言えないがerror() 関数を使うのもあり。

感想

だんだんと面白い事になってきやがった!!!!!!
すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!


僕が考えた演習問題の回答

1.論理演算

2.うるう年

4.caseとifを使わない

5.シーザー暗号
で、"Par Yngvmbhgte Ikhzktffbgz Ftmmxkl" は何を暗号化しているかというと
ghci> caesar 7 "Par Yngvmbhgte Ikhzktffbgz Ftmmxkl"
"Why Functional Programming Matters"
という感じでした。

6.パターンマッチの網羅

1.リストの長さ

2.総和と総積

3.偶数と奇数にわける

5.フィボナッチ数列
前述の、「愚直なフィボナッチ数を求めるプログラム」なので、可成り遅いです。

1.部分適用された関数の型
max 5 :: (Num a, Ord a) => a -> a
takeWhile (<100) :: (Num a, Ord a) => [a] -> [a]
zipWith (+) :: Num a => [a] -> [a] -> [a]

2.コラッツ数列
コラッツ数列自体を求める関数collatz と、
1..100 まででコラッツ数列が最も長いものを求める関数longestLengthOfCollatz

恐らく、答えは97 なのではないかと思います。

3.関数適用と関数合成
関数適用
関数合成
4.ポイントフリースタイル
j をポイントフリースタイルで書き換えるってどうやってやるんですかね……

5.applyTwice
applyTwice applyTwice (+1) 0
->4
applyTwice applyTwice applyTwice (+1) 0
->16
applyTwice applyTwice applyTwice applyTwice (+1) 0
->65536
かような結果が得られる理由:左結合だから?
applyTwice applyTwice applyTwice applyTwice applyTwice (+1) 0 の結果:2^65536

6.fold
?

7.zipWith, scanl と、あの数列
フィボナッチ数のリストを返す。それも無限に。