垂れ流す思考

思考を垂れ流しアウトプットを行うブログ

すごいHaskell楽しく学ぼう!第5章 高階関数

すごいHaskell楽しく学ぼう!Memo

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

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

噂のすごいH本を読んで楽しくHaskellを学んでいこうというコンセプト ふつうのH本が終わったので次はこっち。

第5章 高階関数

サクッといけると思ったが結構重かった。重要な概念なんで仕方ないね。

  • カリー化関数
  • 部分適用

    この二つの動きは完全に覚えようとのこと。

ただこの二つは難しく考えないですんなりわかると思う。

カリー化関数

要は部分適用ができる関数。

部分適用

少ない引数で呼ばれた時に、残りの引数を取る関数になること。

sum'関数で考えると。

sum' :: Int -> Int -> Int
sum' x y = x + y

(sum' 1) -- このように呼ばれた場合
sum' y = 1 + y -- このような形の関数になること

(sum' 1) 2

Haskellは全ての関数がこのカリー化関数なので2つ以上の引数を取る関数は全て部分適用で処理されていると考える。

高階関数 Higher Order Function

関数を引数にしたり戻り値に関数を返したりする関数
wikiを見ればわかるがカリー化なんかは高階関数の一種...らしい。 -> https://ja.wikipedia.org/wiki/高階関数

私見

Haskellの場合カッコ()を省略する書き方が色々あるため、カッコ()を使う場合は上記の部分適用を使うためが多い気がする。

例えばmap関数で全てのリストに3を加える場合

map (+3) [1..10]

上記のように書くが(+3)は要は

+ (x, y)
+ (x, 3)

こんな感じで、二つの引数を取る関数を一つの引数を取る関数にするために使われている。

やっぱり引数を減らして見やすくするのは重要ですわな。
Ruby on Railsで関数定義する時どんどん引数が増えちゃうのはダメだね。
大きい関数ではなく小さい関数の組み合わせってことだな。
そうなるとやっぱ関数名も自明の方が可読性が上がると...
関数脳大事。

ラムダ式

JavaScript使ってたら定番の無名関数のこと。

// JSの場合
const succNum = x => x + 1

succNum(1) // -> 2
-- Haskellの場合
succNum = \ x -> x + 1

succNum 1 -- -> 2

書き方も見ればわかるかな。バックスラッシュが使われているのは \λ に似ているからだそうですよ。

畳み込み(fold)

foldの説明。
これはrubyでいうreduceみたいな動作を指すのかな?
リストの要素を処理して一つの値を返すこと。

Haskellの畳み込み関数は2引数関数(引数を二つ取る関数)と畳み込みに使う値(アキュムレータaccumulator)の初期値、最後にリストを使う。

underscore.jsとかの関数でaccってなんの略称なのか気になって調べた時、よくわかんなかったけど
ようやく納得いった。アキュムレータっていうですね、賢くなったわ。

foldrとfoldlの左右の違いとかよくわからなかったけどGHCiで書いていったらやっとわかったわ。
リストの左右から評価されるっていうのは言葉通り受け取っていいってことね。
あとはラムダ式を使う場合は引数の順番が違うことに注意かな。

map' :: (a -> b) -> [a] -> [b]
-- foldl leftの場合
map' f = foldl (\acc x -> acc ++ [f x]) []
-- foldr rightの場合
map' f = foldr (\x acc -> f x : acc) []

foldl は左からだから アキュムレータ、各要素... foldr は右からだから ...各要素、アキュムレータ てな感じの解釈で。

畳み込みを自分の中で落とし込むのに時間かかったわ〜

$ を使った関数適用

$はカッコを省略できるみたいな解釈だったけど定義見ると納得いった。

($) :: (a -> b) -> a -> b
 $ f x = f x

普通は関数適用の優先度が高いが$最も低い優先度の右結合なので
(map (+3) [1..5]) -> $ map (+3) [1..5]みたいに書ける。
カッコ消せるのはいいよね。

関数合成とポイントフリースタイル

.で関数を合体できるってやつ関数の返り値を別の関数に適用させる時にいいよねって感じ。 適当なサンプルコードを書こうと思ったけどなんかエラーが出てるからダメだ。
パッとHaskellのコードを書けるようになるには修行が足らぬ。

以上

余談

すごいH本p.73のラムダ式の挿絵に当たり前のようにHalf Lifeの主人公ゴードン・フリーマンが描かれててビビった。
胸に入マークが輝いてますね〜(λだけどね)

俺もHalf Lifeやるか〜

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

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

すごいHaskell楽しく学ぼう!第4章

すごいHaskell楽しく学ぼう!Memo

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

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

噂のすごいH本を読んで楽しくHaskellを学んでいこうというコンセプト ふつうのH本が終わったので次はこっち。

第4章Hello 再帰!

再帰処理について説明している章。
再帰recursion
リストなどで一つづつ処理を施している時に再帰を使う。
パターン的に、前章のパターンマッチを利用して、最初に空チェックをして
残りを再帰処理で整形していく。

maximum' :: (Ord a) [a] -> a
maximum' [] = "エラー"
maximum' [x] = x
maximum' (x:xs) = max x (maximum' xs)

パターンマッチは見やすいが、慣れないうちは漏れなく処理の場合分けを書くというのは
頭を使うのでなかなか...
パターンマッチ処理に頭を使わないで書けるぐらい慣れて行きたい。

ふつうのH本もすごいH本も基本的に関数を再実装して確認していく流れが多いので、
手を動かして頭に入れていこうかと。

以上

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

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

すごいHaskell楽しく学ぼう!1~3

すごいHaskell楽しく学ぼう!Memo

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

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

噂のすごいH本を読んで楽しくHaskellを学んでいこうというコンセプト ふつうのH本が終わったので次はこっち。

すごいH本とふつうのH本

言われたとおりふつうのH本からやってよかったな〜と。
すごいH本のかなり序盤で出て来た用語なんかはふつうのH本の後半に出て来たものばかりだから
結構面くらうよね。

すごいH本第1章の内容はふつうのH本の中盤辺りぐらいなので全くのHaskel初心者ならゆっくり行きましょう。
(仕事でHaskellを使うから覚えなきゃ!なんてパターンは稀ですよね?てかそんな人いるの?)

第1章 はじめの第一歩

やっぱりここで注目するのは、

  • リスト内包表記
  • タプル

    でしょうね。 ふつうのH本だと中盤で説明される内容初っ端から来るんで備えましょう。

自分はポンコツRuby使いは型を意識することがあんまないので、リストが同じ型のみとか結構びっくり。
関数型はひたすら関数書いてGHCiで読み込んで確認できるから結構好き。

ふつうのH本読破後なんですんなりいける。

第2章 型を信じろ!

  • 型クラス

    これがまだ曖昧かな。
    JavaだとInterface、Rubyだと該当する概念がないが、型の型。みたいなイメージ。
    なんかもっと独自で定義して使うのかと思ってたけど、既存の型クラスを組み合わせて使うのがメイン。
    それなら書いていけば覚えるか。
    この辺もふつうのH本の中盤にあったんで大丈夫。

第3章 関数の構文

  • パターンマッチ
  • ガード
  • where, let, case キーワードはこれら。

パターンマッチとか書けばわかるけど多言語のswitchとかcase文と違いがわからない。
書き方に関してはすごいH本読みながらコード書いてたら馴染みだした。
パターンマッチと条件分岐の違いが明確になったら関数脳が高まりそう。

以上

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

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

ふつうのHaskellプログラミング 読み終えて

ふつうのHaskellプログラミング

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

噂のすごいH本を読んで楽しくHaskellを学んでいこうというコンセプト

会社のHaskellerさんに
「関数型素人がすごいH本で入門すると中盤で詰むよ」
「初めてならふつうのH本からがおすすめ」
ということなので『ふつうのHaskellプログラミング』からやっていこうかと。

第8章 関数

第9章 型と型クラス

第10章 モジュール

第11章 モナド

結局読み終えました。
なお、第12章以降のwiki開発はしていません。

8~11はHaskellプログラミングをする上で超重要な部分なのでふつうのH本だけで止めずに
他の書籍でも説明を受けて自分に落とし込めようかと。
なのでこの辺は写経しかできないので後回し。
最初の予定通り、すごいH本を読みつつ、Haskell入門関数型プログラミング言語の基礎と実践でWebアプリを実装していこうと。

お盆なんで、すごいH本は読破したいな〜

以上

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング 第6章、第7章

ふつうのHaskellプログラミング

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

噂のすごいH本を読んで楽しくHaskellを学んでいこうというコンセプト

会社のHaskellerさんに
「関数型素人がすごいH本で入門すると中盤で詰むよ」
「初めてならふつうのH本からがおすすめ」
ということなので『ふつうのHaskellプログラミング』からやっていこうかと。

第6章 基本的な値

あんまり書くことはない。
タプルって型はRubyにはないから新鮮。
Haskellで扱う値の説明なので割愛。

Haskellでは関数適用は最も優先順位が高く、どんな演算子よりも優先的に結合する。

関数適用は関数に引数を入れること。

name name 演算子 name name
↓
(name name) 演算子 (name name)

となる。

第7章 基本的な構文

ここも基本的に割愛。
言語仕様はしっかり書いているWebページが多いので、解説はしない。
一応まとめの部分を書いておく。

  • コメント
  • レイアウト(オフサイドルール)
  • if式
  • パターンマッチ(ガード)
  • case式
  • 二項演算子を含む関数定義
  • let式
  • where節
  • パターン束縛

Haskellはif式よりパターンマッチを多用する印象。
case式もパターンマッチやガードを使う書き方。

関数定義以下はブロック構文みたいなイメージ。
スコープを狭める書き方。

第8章 関数はかなり重要な説明らしいので、ゆっくり理解しよう。

以上

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング 第5章

ふつうのHaskellプログラミング

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

噂のすごいH本を読んで楽しくHaskellを学んでいこうというコンセプト

会社のHaskellerさんに
「関数型素人がすごいH本で入門すると中盤で詰むよ」
「初めてならふつうのH本からがおすすめ」
ということなので『ふつうのHaskellプログラミング』からやっていこうかと。

第5章 遅延評価

ここからHaskellの全貌と言うことで第二部になる。
小難しくなりそうだが気合いを入れる。

評価(evaluation)

評価(evaluation)はプログラミングの世界では「計算」と同義。

Haskellの評価過程は置き換えモデル(substitution model)を使って表現できる。
仮引数を実引数に置き換えながら関数の適用を書き換えること。
仮引数と実引数の説明はここ


簡約(reduction)

square n = n * n

上記の関数をsquare (1 + 3)で評価する場合は、

square (1 + 3)
→ (1 + 3) * (1 + 3)
→ 4 * 4
→ 16

と言う流れ。
この作業を簡約(reduction)と言う。


最内簡約と最外簡約

square (1 + 3)(1 + 3) * (1 + 3)と簡約されるのが最外簡約
square (1 + 3)square 4と簡約されるのが最内簡約

最内簡約はJavaなどの言語。引数から簡約していく。
遅延評価のHaskellは最外簡約である。


グラフ簡約

ここは少しわかりずらいが、Haskellの遅延評価の特徴のもう片方。
(1 + 3) * (1 + 3)の足し算の部分の計算は一度しか行われないってこと。
しっかり書くと引数の評価は一度だけ評価される。
細かいけど忘れずに。


評価に必要ない式なんかは評価されない。
データ構造も必要な部分だけ評価される。
length [(1+1), (2+2), (3+3)]とあった場合、
(1+1) (2+2) (3+3)これらはlength関数の評価には必要ないので評価されません。効率的だね。


遅延評価の利点と欠点

上記三つが利点。

  • 思った順番で操作を実行するのが難しい
  • デバッグしにくい

これが欠点。

一長一短だけどそう言うもん。

デバッグに関しては使っていけばそこまで気にならなくなる?みたいな?


ここまで来たがコード書く量が減って来たな。もうちょっと手を動かさなきゃね。
今日はここまで。

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング 第4章

ふつうのHaskellプログラミング

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

噂のすごいH本を読んで楽しくHaskellを学んでいこうというコンセプト

会社のHaskellerさんに
「関数型素人がすごいH本で入門すると中盤で詰むよ」
「初めてならふつうのH本からがおすすめ」
ということなので『ふつうのHaskellプログラミング』からやっていこうかと。

第4章 Haskellの基礎(3)モジュールと総合演習

module と import

Haskellはmodule単位で分割される。
そのmoduleを利用するときにimport宣言(import declaration)を使い、読み込む。

import System

main= do args <- getArgs
         putStrLn $ unwords args

ふつうのH本の上記echo.hsのソースコードだが、import Systemは名前が変わっている。
ここにdocを置いておく。

import           System.Environment

main = do args <- getArgs
          putStrLn $ unwords args

Main moduleとPrelude module

すべての関数や変数はmoduleに属している。
ではmainと言う変数は何に属しているか?
Main moduleになる。
明示的に宣言もできるが宣言をしない場合、暗黙のうちにそのファイルがMain moduleになり、main変数だけを公開する。

そして基本的な型や関数が定義されているモジュールがPrelude moduleとなる。
全てのモジュールは暗黙のうちにPrelude moduleをインポートしている。

あとは総合演習として関数の説明なので、割愛。

以上

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門