SlideShare a Scribd company logo
Swi$による
関数型プログラミング
超入門
2015年11月14日
藤本尚邦 / Cocoa勉強会(関東) #75
1
自己紹介
• 藤本尚邦 (@%isa)
• h+ps://github.com/%isa
• フリーランスプログラマー
• RubyCocoaフレームワーク原作者
• Mac開発歴、薄く長く約25年
• iOS開発歴、約1年
2
アジェンダ
• 始めに
• 総和・総乗
• 総和・総乗の抽象化
• SequenceTypeプロトコル
• 関数型プログラミングとは
• まとめ
3
始めに
• 関数型プログラミング1
ってよく聞くけど何それ?
• C言語には関数があるから関数型プログラミング
ができるの?
• map とか reduce って何?どうやって使うの?
このような立ち位置の人を想定して、関数型プログ
ラミング風の考え方の一端を紹介します。
1
英語で Fanc%onal Programming といいます。短縮して FP と呼ばれています。
4
始めに
私自身は、語れるほどFPを理解しているわけではあ
りません。ただし、以前自分でもそれと気付かずに
FPを学んだことがあります。
その経験をもとに発表の構成を考えました。
5
総和・総乗
6
総和・総乗
定義1:
7
総和・総乗
func sum(n: Int) -> Int {
var accumulator = 0
for i in 1...n { accumulator += i }
return accumulator
}
func prod(n: Int) -> Int {
var accumulator = 1
for i in 1...n { accumulator *= i }
return accumulator
}
ループと代入を使った手続き的なプログラム
8
総和・総乗
定義2:
9
総和・総乗
func sum(n: Int) -> Int {
return n == 0 ? 0 : n + sum(n - 1)
}
func prod(n: Int) -> Int {
return n == 0 ? 1 : n * prod(n - 1)
}
再帰呼出を使ったFPの香り漂うプログラム
10
総和 (番外編)
ガウス(小3)による総和のプログラム:
func sum(n: Int) -> Int {
return n * (n + 1) / 2
}
総和についてはガウスの方法が賢いやり方だが、総
乗への応用は多分きかない。
11
総和・総乗の抽象化
12
総和・総乗の抽象化
2つの関数の似ている点・違う点を探してみよう:
func sum(n: Int) -> Int {
return n == 0 ? 0 : n + sum(n - 1)
}
func prod(n: Int) -> Int {
return n == 0 ? 1 : n * prod(n - 1)
}
13
総和・総乗の抽象化
2つの関数の違う点:
• n==0のとき、前者は0を返し後者は1を返す
• n!=0のとき、前者は足し算で後者は掛け算
14
総和・総乗の抽象化
元の引数nに加えて、総和・総乗で違う以下の2点:
• n==0のときの値 (初期値)
• n!=0のときの計算 (関数)
を引数にすれば、同じパターンの計算に使えるより
汎用的な関数が出来上がるはずです。
15
総和・総乗の抽象化
typealias IntCombinator = (Int, Int) -> Int
func reduce(n: Int, initial: Int, combine: IntCombinator) -> Int
{
if n == 0 {
return initial
} else {
return combine(
n,
reduce(n - 1, initial: initial, combine: combine))
}
}
総和・総乗のパターンを抽象化した関数 reduce
16
総和・総乗の抽象化
関数reduceを使って総和・総乗を計算してみよう:
// ふたつの整数の和算・乗算をする関数を定義
func add2(n: Int, m: Int) -> Int { return n + m }
func mul2(n: Int, m: Int) -> Int { return n * m }
reduce(10, initial: 0, combine: add2)
reduce(10, initial: 1, combine: mul2)
17
総和・総乗の抽象化
関数reduceを使って総和・総乗を計算してみよう(2):
// 無名関数(関数リテラル)を直接渡す
reduce(10, initial: 0, combine: { $0 + $1 })
reduce(10, initial: 1, combine: { $0 * $1 })
// Ruby風シンタックスシュガー
reduce(10, initial: 0) { $0 + $1 }
reduce(10, initial: 1) { $0 * $1 }
18
総和・総乗の抽象化
Swi$では + や - も関数なので引数に渡せます:
// モダンな関数型プログラミング風
reduce(10, initial: 0, combine: +)
reduce(10, initial: 1, combine: *)
add2 や mul2 のような関数を定義する必要はありま
せんでした。
19
総和・総乗の抽象化
ここまで、関数 reduce の定義にいたる道筋を示す
ことで、関数型プログラミングの考え方の土台を簡
単に紹介しました。
今回は、関数を引数にとる関数を題材にしました
が、関数を返す関数を使ったプログラミングなどさ
らに奥深い世界が存在しています。
20
SequenceTypeプロトコル
21
SequenceTypeプロトコル
for item in collection {
statements
}
Swi$の For-In構文では、繰り返しの対象となる
collection が SequenceType プロトコルに適合
している必要があります。
22
SequenceTypeプロトコル
代表的なインスタンスメソッド:
• filter
• map
• reduce
• sort
23
SequenceTypeプロトコル
filter
引数で与えられた関数で各itemを調べ、trueを返
したitemのみによる配列を返します。
(1...10).filter { $0 % 2 == 0 } // => [2, 4, 6, 8, 10]
(1...10).filter { $0 % 2 != 0 } // => [1, 3, 5, 7, 9]
24
SequenceTypeプロトコル
map
数学で写像(mapping)と呼ばれているものに相当しま
す。各itemに関数を適用した結果の配列を返しま
す。
(1...5).map { $0 * 2 } // => [2, 4, 6, 8, 10]
(1...5).map { String($0) } // => ["1", "2", "3", "4", "5"]
(1...5).map(String.init) // => ["1", "2", "3", "4", "5"]
25
SequenceTypeプロトコル
reduce
現実的・汎用的な本物のreduceです23
。
(1...10).reduce(0, combine: +) // Swift的な総和の計算
(1...10).reduce(1, combine: *) // Swift的な総乗の計算
["aa", "bb", "cc"].reduce("") { "($0)/($1)" }
// => "/aa/bb/cc"
3
言語によっては inject (Ruby, Smalltalkなど) や fold (Haskellなど) と呼ばれています。
2
SequenceTypeではitemの型も変数なので、Int型に限定せず扱うことができます。
26
SequenceTypeプロトコル
sort
itemを比較関数で並べ変えた配列を返します。item
がComparableプロトコルに適合している場合は、
比較関数を省略できます。
[5,4,3,2,1].sort() // => [1, 2, 3, 4, 5]
(1...5).sort(>) // => [5, 4, 3, 2, 1]
27
SequenceTypeプロトコル
次のようなPersonと名簿が定義されているとします:
struct Person {
let name: String
let age: Int
let sex: Sex
enum Sex { case Male, Female, Others }
}
let BABYMETAL: [Person] = [
Person(name: "中元すず香", age: 17, sex: .Female),
Person(name: "菊地最愛", age: 16, sex: .Female),
Person(name: "水野由結", age: 16, sex: .Female),
Person(name: "青山英樹", age: 29, sex: .Male),
Person(name: "BOH", age: 33, sex: .Male),
Person(name: "大村孝佳", age: 31, sex: .Male),
Person(name: "藤岡幹大", age: 34, sex: .Male),
]
28
SequenceTypeプロトコル
//: BABYMETAL女子の名前
BABYMETAL
.filter { $0.sex == .Female }
.map { $0.name }
//: BABYMETAL全員の平均年齢
Float(BABYMETAL
.map { $0.age }
.reduce(0, combine: +)) / Float(BABYMETAL.count)
29
関数型プログラミングとは
30
関数型プログラミングとは
Swi$の特徴4
:
• 関数がファーストクラスオブジェクト
• 関数を引数や返り値として扱える
• 関数と普通の変数の名前空間が一緒
4
JavaScript はこの特徴を持つプログラミング言語の代表例
31
関数型プログラミングとは
代入を使わない限り、同じ引数による同じ手続きの
呼び出しは2回評価しても同じ結果になるので、手
続きは数学関数の計算とみなすことができる…まっ
たく代入を使わないプログラミングは、そのため関
数型プログラミング(func%onal programming)と呼ばれ
ている。
— Structure and Interpreta%on of Computer Programs
(真鍋宏史日本語訳版) より
32
関数型プログラミングとは
「計算機プログラムの構造と解釈」(通称SICP):
1. 手続きを用いた抽象化の構築 (高階関数)
2. データを用いた抽象化の構築 (データ構造)
3. モジュール性、オブジェクト、状態 (代入初登場)
4. メタ言語抽象化 (プログラミング言語の実装)
5. レジスタマシンによる計算 (CPU・コンパイラ)
33
まとめ
Swi$は手続き型プログラミングと関数型プログラミ
ングの双方のメリットを利用できるバランスの良い
言語です。大いに活用しましょう!
34
参考文献
• Structure and Interpreta.on of Computer Programs
(SICP) 真鍋宏史日本語訳版
• The Swi< Programming Language (Swi< 2.1)
• Swi< Standard Library Reference
35
Thank you!2015年11月14日
藤本尚邦 / Cocoa勉強会(関東) #75
36

More Related Content

Swiftによる関数型プログラミング超入門