2015-12-30 『ふつうのHaskellプログラミング』 もくもく読書会(第8章) の振り返り
ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門
- 作者: 青木峰郎,山下伸夫
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2014/10/05
- メディア: オンデマンド (ペーパーバック)
- この商品を含むブログを見る
第8章 関数
値としての関数
関数束縛
- 関数名を変数とし、その引数及び処理の定義の関数を束縛すること
例:
square n = n * n
- 変数:square
- 関数:n = n * n
高階関数
- 関数を引数として受け取ったり、戻り値として返したりする関数
無名関数
- 変数を束縛しない関数
- 最初の「\」をラムダと言う
- 引数は複数設定可
\引数1 引数2 … -> 式
例:
\ x y -> x + y
無名関数のパターンマッチ
- 無名関数の引数でもパターンマッチが使える
- 無名関数で使えるパターンは一つだけ
関数合成
- 複数の関数を合わせて新しい関数を作ること
- 「.」を使って2つの関数を合成できる
numberOfLines :: String -> Int numberOfLines cs = length $ lines cs
は、関数合成を使うと下記に書き換えれる
numberOfLines :: String -> Int numberOfLines = length . lines
(.)関数の定義
(.) :: (b -> a) -> (a -> b) -> (a -> c)
- (f . g) にxを与えた
(f . g)x
と、f(g(x))
は同じことを意味する
コメント
numberOfLines cs = length $ lines cs
と
numberOfLines = length . lines
で、なぜ関数合成のほうは引数「cs」がなくていいのか理由がわからない
引数「cs」が不要なら
numberOfLines = length $ lines
でも、良さそうな気がするが、これだとコンパイルエラーになる
それにその前にでてきた関数束縛と無名関数の説明に従うと
numberOfLines = \() -> length . lines
となるのでは? ただ、これはコンパイルエラーになるし、そもそも引数なしで処理ができるはずないからおかしい。
↓
など考えていたが「部分適用」のとこで説明があった
部分適用
- 引数を一度にすべて渡さず、一部だけ渡しておくこと
- Haskellでは複数の引数を取る関数に1つだけ引数を渡してもエラーにはならない
- Haskellでは真の意味で複数の引数を取る関数はない
→ 1つの引数を取り他の引数を取る関数を返す高階関数とみなすことができる - セクション:部分適用を使い引数をひとつだけ渡した二項演算子
- 「-」は
(-1)
でも(- 1)
でも数値「-1」とみなされセクションをつくることはできない (subtract 1)
で1を引くセクションができる
コメント
関数合成の
numberOfLines :: String -> Int numberOfLines = length . lines
ではlength . lines
がセクションとなり引数を省略できるのはわかった。
関数束縛と無名関数で
numberOfLines = length . lines = \cs -> length . lines cs
と言うことなのだろう。
ただ、そうなると
numberOfLines = length $ lines
これがダメな理由がわからない。
length $ lines
もセクションと見れるのでは?
コメントより
($) :: (a -> b) -> a -> b (.) :: (b -> c) -> (a -> b) -> a -> c
型から見ると($)は、最初の引数が、関数を示す(a -> b)であり 最後の引数が、最初の引数の関数の引数の型であるa型であるような、二項演算子となる。
と言うことで($)の場合、lengthが(Foldable t => t a -> Int)だがlinesはa型を持つFoldableでないといけないが関数なので、引数の省略は不可。
> :t length length :: Foldable t => t a -> Int
ポイントフリースタイル
- ポイントフリースタイルとは、関数を関数で定義するコーディングスタイル
- 「point」は圏論の用語でHaskellでいう値を示す
- ポイントフリースタイルの場合、型宣言はつけるべき
→ 引数がないので、コードが非常にわかりづらくなる
練習問題
- 空白かどうかについて独自に関数を定義していたが
import Data.Char
でisSpace
と言う関数があった - 問題にでてくる文字列はここでは読み込んだファイルの複数行の文字列の意味ではなく一行の文字列のこと
※ 最初に文字列がファイルから読み込んだ文字列のことかと思い、下記のように書いていた
lstrip = unlines . (fmap (dropWhile isSpace)) . lines
でハマったのが
lstrip = unlines $ (fmap (dropWhile isSpace)) . lines
unlines の後の「$」に気づいていなくてエラーがでていたこと。 「$」演算子があるとポイントフリースタイルが使えないのは何故?
これも上記同様に($)の定義が下記であるため
($) :: (a -> b) -> a -> b