SlideShare a Scribd company logo
第3回 関数型入門
TypeScript & 関数型講座
今講座のコンセプト
関数型プログラミングを知らない人には
関数型プログラミングとはどういうものかを知ってもらう
関数型プログラミングを実践している人には
改めて、関数型プログラミングのメリットを整理する
関数型プログラミングとは
一言でいうならば
副作用を分離すること!
関数型プログラミング入門
プログラマが関数型プログラミングについて語るとき、めまいを起こしそうな数の“関数型”的な特徴に
ついて言及します。彼らは変更不能なデータや第一級関数や末尾呼び出し最適化について述べま
す。これらは関数型プログラミングを助ける言語の特徴です。そして彼らはマッピング、リデューシン
グ、パイプライン化、再帰法、カリー化、そして高階関数の使用法についての話に移ります。これらは
関数型のコードを書くのに使われるプログラミングテクニックです。それからさらに並列化や遅延評価、
決定性について言及するでしょう。これらは、関数型プログラムの長所です。
これらを全て忘れてください。関数型コードを特徴づけるのは、「副作用がない」という1
点です。関数型のコードは現在の関数の外部に存在するデータに頼りません。そして現在の関数の
外部に存在するデータを変更しません。
その他の“関数型”なものは、全てこの特性から派生しています。このことを学習の手がかりにしてくだ
さい。
参考資料
副作用とは?
諸説あるが、
- プログラムの状態を変化させ、それ以降で得られる結果に影響を与えること
- 変数の再代入
- 破壊的なメソッドの使用(これも内部的には変数への再代入をしている)
- 本来想定している作用とは別に発生する作用
- 例外 (約束事「関数シグネイチャー」を無視するため)
- 常に同じ値が返ってくるとは限らないこと
- DBへの読み書きなどの I/O実行(プログラム外部の状態に依存している)
- 乱数生成
参照透過性と純粋関数
参照透過性とは
- 同じ条件を与えれば必ず同じ結果が得られる性質
純粋関数とは
- 参照透過性を持つ
- 他のいかなる機能の結果にも影響を与えない(副作用を持たない)
- 上記のような性質を持つ関数
関数型プログラミングでは、純粋関数を組み合わせて処理のパイプラインを構築していく
ちなみに、参照透過性は関数のみならず、オブジェクトにも適用可能である。状態を持たない
オブジェクトは、ある意味で純粋関数とみなせる
副作用を局所化する
副作用を完全に無くすと何も出力ができなくなってしまうため、システムとしての意味がなく
なってしまう。そのため、副作用を完全になくすのではなく局所化し、影響範囲を極力少なくす
ることが関数型プログラミングの目的
pure
pure
pure
pure
pure
pure
pure
pure
pure
pure
pure
pure
impure
impure
impure
関数型プログラミングのメリット
プログラミングの分かりやすさ
- 再代入、破壊的なメソッドが使用しないことによるメリット
- 変数束縛時点で値が決まる。そのため、変数束縛以降の処理を細かく追わなくて
もよい
- 例外が発生しないことによるメリット
- メソッドシグネイチャー以外を考慮しなくてよい
- 補足:関数型プログラミングでは従来例外として表現していたものを、文脈として
表現する。文脈として表現されたものは、例外と異なり、型レベルで対処を強要す
るため、抜け漏れが無くなる
- 常に同じ値が帰ってくることによるメリット
- 結果を容易に推論できる
テストのしやすさ
- 入力が同じなら出力も同じ
副作用がある例
並列処理のしやすさ
- 状態を持っていると、スレッド間での取り回しが難しくなる
スレッド1 スレッド2
参照
参照
更新
別スレッドの処理に影
響される
スレッド1 スレッド2
参照値更新後の
オブジェクト生成
別スレッドの処理に影
響されない
参照
参照
状態なし状態あり
オブジェクト指向との関係
オブジェクト指向とは、データとその振る舞いをオブジェクトに閉じ込めるという考え方
諸説あると思うが、個人的には「データとその振る舞いをオブジェクトに閉じ込める」というのが
本質で、そのデータが可変かどうかは本質的なものではないと思うので、オブジェクト指向と
は両立すると思う
宣言型プログラミングとの関係
宣言型プログラミングは「対象の性質を宣言してプログラムを構成する」ことで特徴づけられ
る。すなわち、出力を得る方法ではなく、出力の性質・あるべき状態を記述することを「宣言
型」と称する。(wiki)
関数型プログラミングは宣言型プログラミングの一部だとされている(wiki)が、そんな事はな
いと思う(相性がいいのは認める)
関数型でも手続き的に書けるし、オブジェクト指向でも宣言的に書ける
val users: Seq[User] = userRepository.getAll()
// 出力する方法ではなく、出力の性質を記述している
val longNameUser = users.filter(user => 10 < user.name.length)
関数型言語はあくまで関数型プログラミングに有利な特徴を備えているだけ。重要なのは、副
作用を分離すること。
部分的に関数型的に書いていくだけでも、メリットは享受できる
関数型言語ではなくても
関数型プログラミングは可能
関数型のプログラミングテクニック
再代入禁止
関数型プログラミングでは、代入ではなく、束縛を行う(宣言時以降変更されない場合、
束縛という)
Scala では val、TypeScript では const、Java では final 修飾子で表現される
再帰関数
for や while でのループは再代入を伴うため、関数型プログラミングでは使用されない
(拡張 for 文は再代入が伴わないので、OK? )
その代わりに再帰関数で実現する(初めはとっつきにくいけど、慣れると for 文等より
理解しやすい)
def sum(init: Long, from: Int, to: Int): Long = {
if (from == to) init + to
else sum(init + from, from + 1, to)
}
sum(0, 1, 10) // 55
再帰関数の作り方
1. 必要な情報(関数シグネイチャー)を考える
2. 停止条件を考える
3. それ以外の場合の処理を考える
def sum(init: Long, from: Int, to: Int): Long = { // ①
if (from == to) init + to // ②
else sum(init + from, from + 1, to) // ③
}
末尾再帰最適化
再帰関数は普通に実装すると、スタックオーバーフローする可能性がある
これを回避するため、言語によっては末尾再帰最適化に対応しているものもある
末尾再帰最適化とは、再帰関数内で再帰呼び出しを行う部分が関数で最後に評価さ
れる場合、コンパイラが内部的に for や while に変換し、スタックオーバーフローしな
いようにしてくれるというもの
def sum(init: Long, from: Int, to: Int): Long = {
if (from == to) init + to
else sum(init + from, from + 1, to) // sum の呼び出しは最後に評価されるため、末尾再帰最適化対
象
}
末尾再帰最適化の対応状況
対応してない言語では再帰回数が多い再帰関数は使用しない方が良いかも
Scala 対応
Java 非対応
JavaScript 非対応?
高階関数
再帰関数を使わなくちゃいけないのはわかったけど、毎回書くの面倒だよね
そこで、map, foldLeft(reduce)ですよ
for や while を使いたい場面には共通点がある
- あるリストに対して、その全ての値を変換する場合
- リストの全ての要素を走査して、単一の値を算出する場合
こうした振る舞いを共通化したのが、map や foldLeft
map
リストの map は、その全ての値を変換する処理を共通化したもの
リストに格納されている値を受け取り、単一の値を返す関数を引数に取る
(1 to 10).map(x => x * 2) // Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
foldLeft
foldLeft(JavaScript とかだと reduce)は、リストの全ての要素を走査して、単一の値を
算出する処理を共通化したもの
初期値と、そこまでの累積値及びリストに格納されている値を受け取り、単一の値を返
す関数を引数に取る
※ちなみに Java の reduce は並行処理を許可する関係で、モノイド則を満たす型でし
か使えないようなので注意
(1 to 10).foldLeft(0)((acc, x) => acc + x) // 55
高階関数
map や foldMap のように、関数を引数に取る関数、もしくは関数を返す関数を高階関
数と呼ぶ
カリー化
カリー化とは、複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値
が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(ある
いはその関数のこと)。(wiki)
def add(x: Int, y: Int): Int = {
x + y
}
// カリー化した場合
def add(x: Int)(y: Int): Int = {
x + y
}
function add(x: number): (y: number) => number { // TypeScript では
return y => x + y
}
部分適用
部分適用とは、カリー化された関数に一部のみ引数を適用すること。
これにより、処理の冗長性を排除できる
def add(x: Int, y: Int): Int = {
x + y
}
// 第一引数に必ず 1 が渡される場合
val test1 = add(1, 1)
val test2 = add(1, 2)
val test3 = add(1, 3)
val test4 = add(1, 4)
def add(x: Int)(y: Int): Int = {
x + y
}
val add1 = add(1) _ // add1: Int => Int = <function>
val test1 = add1(1)
val test2 = add1(2)
val test3 = add1(3)
val test4 = add1(4)
まとめ
関数型プログラミングとは副作用を分離するプログラミングスタイルである
関数型プログラミングには以下のメリットがある
- わかりやすい
- テストしやすい
- 並列処理しやすい
関数型プログラミングは関数型言語でなくてもできる
関数型プログラミングテクニック
- 変数束縛
- 再帰関数と高階関数
- カリー化と部分適用

More Related Content

PDF
ドキュメントを作りたくなってしまう魔法のツールSphinx
PDF
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
PDF
ドメイン駆動設計のためのオブジェクト指向入門
PDF
ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8
PPT
ドメインロジックの実装方法とドメイン駆動設計
PDF
実践的な設計って、なんだろう?
PDF
ドメイン駆動設計 本格入門
PDF
クロージャデザインパターン
ドキュメントを作りたくなってしまう魔法のツールSphinx
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8
ドメインロジックの実装方法とドメイン駆動設計
実践的な設計って、なんだろう?
ドメイン駆動設計 本格入門
クロージャデザインパターン

What's hot (20)

PDF
いまさら聞けないパスワードの取り扱い方
PDF
オブジェクト指向の設計と実装の学び方のコツ
PDF
マイクロにしすぎた結果がこれだよ!
PDF
MapReduce入門
PDF
PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。
PDF
ドメイン駆動設計 基本を理解する
PPTX
Redmineカスタムフィールド表示改善
PDF
デキるプログラマだけが知っているコードレビュー7つの秘訣
PDF
コミュニティ分類アルゴリズムの高速化とソーシャルグラフへの応用
PPTX
テストコードの DRY と DAMP
PDF
関数プログラミング入門
PDF
ドメインオブジェクトの見つけ方・作り方・育て方
PDF
信頼性とアジリティを同時に上げろ!モノタロウのカナリアリリース導入.pdf
PDF
データベース設計徹底指南
PDF
いつやるの?Git入門
PDF
文字コードに起因する脆弱性とその対策(増補版)
PDF
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
PDF
オブジェクト指向できていますか?
PDF
JenkinsとSeleniumの活用事例
PDF
Building the Game Server both API and Realtime via c#
いまさら聞けないパスワードの取り扱い方
オブジェクト指向の設計と実装の学び方のコツ
マイクロにしすぎた結果がこれだよ!
MapReduce入門
PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。
ドメイン駆動設計 基本を理解する
Redmineカスタムフィールド表示改善
デキるプログラマだけが知っているコードレビュー7つの秘訣
コミュニティ分類アルゴリズムの高速化とソーシャルグラフへの応用
テストコードの DRY と DAMP
関数プログラミング入門
ドメインオブジェクトの見つけ方・作り方・育て方
信頼性とアジリティを同時に上げろ!モノタロウのカナリアリリース導入.pdf
データベース設計徹底指南
いつやるの?Git入門
文字コードに起因する脆弱性とその対策(増補版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
オブジェクト指向できていますか?
JenkinsとSeleniumの活用事例
Building the Game Server both API and Realtime via c#
Ad

Similar to TypeScript & 関数型講座 第3回 関数型入門 (20)

PDF
関数プログラミング入門
PDF
たのしい関数型
PDF
関数型プログラミング in javascript
PDF
Functional Programming in Swift
PDF
磯野ー!関数型言語やろうぜー!
PDF
Scalaプログラミング・マニアックス
PDF
たのしい高階関数
PDF
F#入門 ~関数プログラミングとは何か~
PDF
【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 1 章
PDF
関数型軽い紹介
PDF
関数型プログラミング入門 with OCaml
PDF
関数型都市忘年会『はじめての函数型プログラミング』
PDF
「再代入なんて、あるわけない」 ~ふつうのプログラマが関数型言語を知るべき理由~ (Gunma.web #5 2011/05/14)
PPTX
純粋関数型アルゴリズム入門
PDF
オブジェクト指向開発におけるObject-Functional Programming
PDF
Scalaで学ぶ関数型超入門
PDF
関数モデル 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第8回】
PDF
Swiftによる関数型プログラミング超入門
PDF
命令プログラミングから関数プログラミングへ
PDF
関数型プログラミング入門 for Matlab ユーザー
関数プログラミング入門
たのしい関数型
関数型プログラミング in javascript
Functional Programming in Swift
磯野ー!関数型言語やろうぜー!
Scalaプログラミング・マニアックス
たのしい高階関数
F#入門 ~関数プログラミングとは何か~
【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 1 章
関数型軽い紹介
関数型プログラミング入門 with OCaml
関数型都市忘年会『はじめての函数型プログラミング』
「再代入なんて、あるわけない」 ~ふつうのプログラマが関数型言語を知るべき理由~ (Gunma.web #5 2011/05/14)
純粋関数型アルゴリズム入門
オブジェクト指向開発におけるObject-Functional Programming
Scalaで学ぶ関数型超入門
関数モデル 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第8回】
Swiftによる関数型プログラミング超入門
命令プログラミングから関数プログラミングへ
関数型プログラミング入門 for Matlab ユーザー
Ad

TypeScript & 関数型講座 第3回 関数型入門