関数プログラミング
ことはじめ (再)
Scala 関西 Summit 2018
おことわり
• Scala関西 Summit 2017で木虎直樹さんによっ
て行なわれたハンズオンの再演です
• 2016, 2015 でも行ないました
• 厳密な正確性よりもわかりやすさを重視
• 資料: https://bit.ly/scalaks2018ho
Scala 難しい

という意見を聞くことがあります。
Scala は本当に難しい?
複数の要因を一緒にして
いない?
•Scala の言語仕様が難しい
•関数プログラミングが難しい
•型システムが難しい
Scala の難しさって?
•関数プログラミングが難しい
• 命令プログラミングのパラダイムとい
う先入観をもって、関数プログラミン
グのパラダイムを学ぶ難しさ
• 関数型言語が基礎とする数学概念の難
しさ
• プログラミングの難しさ
本日のテーマ
•関数プログラミングが難しい
• 命令プログラミングのパラダイムとい
う先入観をもって、関数プログラミン
グのパラダイムを学ぶ難しさ
• 関数型言語が基礎とする数学概念の難
しさ
• プログラミングの難しさ
本日のテーマ
関数プログラミングのパラダ
イムを理解するきっかけを掴
む
本日のゴール
関数プログラミングの関数
数学でいうところの関数
f(x) = x + 1
だから Scala では
•関数定義に = (イコール) を使用
•関数内で最後に評価された値が返る
Which
is
better?
def f(x: Int) {x + 1}
def f(x: Int) = x + 1
def f(x: Int) = return x + 1
※注: 上の 2つは正しいコードではありません。
関数プログラミング (狭義)
• 変更可能 (mutable) な変数
• 再代入
• ループなどの命令型の制御
を使わずにプログラミングすること
お願い
現時点での関数プログラミングに対する疑問を
教えてください。
命令プログラマの疑問
再代入やループなしにどうやって書くのか?
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
命令プログラミング
具体的な手順を記述
関数プログラミング
対象の性質を定義
プログラミングの関心事
関数プログラミングでの問題へのアプローチ
問題を抽象化・一般化
準備
• https://bit.ly/scalaks2018intellij
• IntelliJ のセットアップが上手くいかな
い場合はこちら
• https://bit.ly/scalaks2018jupy (ハンズ
オンのJupyter notebook)
• https://scastie.scala-lang.org/ (Web
ブラウザー上でScalaを実行)
問題
ある自然数を引数にとり、当該自然
数以下の自然数の合計を返す関数を
定義せよ
命令プログラミング
• 1からnまでカウントアップ
• カウントアップした数字を足し込んでいく
int f(int n) {
int total = 0;
for (int i = 1; i <= n; i++) {
total += i;
}
return total;
}
例示して性質を抽出
f(1) = 1
f(2) = 1 + 2
f(3) = 1 + 2 + 3
...
f(n) = 1 + 2 + 3 + ... + n
具体から抽象へ
f(1) = 1
f(2) = f(1) + 2
f(3) = f(2) + 3
...
f(n) = f(n - 1) + n
f(1) = 1
f(n) = f(n - 1) + n
def f(n: Int): Int =
if (n == 1) 1
else f(n - 1) + n
f(1) = 1
f(n) = f(n - 1) + n
問題
指定された自然数の階乗を返す関数
を定義せよ
階乗
f(0) = 1
f(1) = 1
f(2) = 2 * 1
f(3) = 3 * 2 * 1
...
f(n) = n * ... * 3 * 2 * 1
階乗 (一般化)
f(0) = 1
f(1) = 1 * f(0)
f(2) = 2 * f(1)
f(3) = 3 * f(2)
...
f(n) = n * f(n - 1)
f(0) = 1
f(n) = n * f(n - 1)
def f(n: Int): Int =
if (n == 0) 1
else n * f(n - 1)
フィボナッチ数列の n 項目の値を返
す関数の定義は?
1, 1, 2, 3, 5, 8, 13, …
フィボナッチ数
f(0) = 0
f(1) = 1
f(2) = 0 + 1
f(3) = 1 + 1
f(4) = 1 + 2
f(5) = 2 + 3
...
フィボナッチ数
f(0) = 0
f(1) = 1
f(2) = f(0) + f(1)
f(3) = f(1) + f(2)
f(4) = f(2) + f(3)
f(5) = f(3) + f(4)
...
f(n) = f(n - 2) + f(n - 1)
f(0) = 0
f(1) = 1
f(n) = f(n - 2) + f(n - 1)
def f(n: Int): Int =
if (n == 0) 0
else if (n == 1) 1
else f(n - 2) + f(n - 1)
問題
指定されたリスト内の数の合計を返す
関数 sum を定義せよ
def sum(ints: List[Int]): Int
リストの定義
•空リスト Nil はリスト
•head が要素、tail がリストなら
head :: tail もリスト
3
Nil
Nil::
3 :: Nil2 ::
2 :: 3 :: Nil1 ::
自身を使って定義されたデータ型
•再帰的なデータ型
•自己参照をするデータ型
再帰的なデータ構造の場合は

その構造に沿って計算する
データ構造
Nil
Nil::8
8 :: Nil::2
2 :: 8 :: Nil::1
1 :: 2 :: 8 :: Nil::5
5 :: 1 :: 2 :: 8 :: Nil
例示
sum(5 :: 1 :: 2 :: 8 :: Nil)
sum( )

= + sum( )
sum( ) = + sum( )
sum( ) = + sum( )
sum( ) = + sum( )
sum( ) = 0Nil
8 :: Nil
2 :: 8 :: Nil
5 :: 1 :: 2 :: 8 :: Nil
Nil8
8 :: Nil2
2 :: 8 :: Nil1
5 1 :: 2 :: 8 :: Nil
1 :: 2 :: 8 :: Nil
一般化
sum( ) = + sum( )
sum( ) = + sum( )
sum( ) = + sum( )
sum( ) = 0Nil
8 :: Nil
2 :: 8 :: Nil
Nil8
8 :: Nil22 :: 8 :: Nil
1
要素
head
リスト
要素
head
リスト
tail
リスト
要素
head
リスト
tail
リスト
head が要素、tail がリストなら head :: tail もリスト
1 :: 2 :: 8 :: Nil
リスト
リスト
tail
sum(Nil) = 0
sum(head :: tail) = head + sum(tail)
def sum(list: List[Int]): Int =
if (list.isEmpty) 0
else list.head + sum(list.tail)
def sum(list: List[Int]): Int = list match {
case Nil => 0
case head :: tail => head + sum(tail)
}
問題
指定されたリスト内の数を掛け合わせ
た値を返す関数 product を定義せよ
def product(ints: List[Int]): Int
問題
指定されたリスト内の最大値を返す関
数 max を定義せよ
def max(ints: List[Int]): Int
問題
指定されたリストを逆順に並び替えて
返す関数 reverse を定義せよ
def reverse(ints: List[Int]): List[Int]
問題
指定されたリストの長さを返す関数
length を定義せよ
def length(ints: List[Int]): Int
末尾再帰
def sum(list: List[Int]): Int = list match {
case Nil => 0
case head :: tail => head + sum(tail)
}
def sum(list: List[Int]): Int = {
def loop(acc: Int, l: List[Int]): Int = l match {
case Nil => acc
case head :: tail => loop(acc + head, tail)
}
loop(0, list)
}
命令プログラミング
具体的な手順を記述
関数プログラミング
対象の性質を定義
プログラミングの関心事
お薦めのリソース
• Functional Programming Principles in Scala

Scala の作者である Martin Odersky 教授によ
る関数プログラミングの講義

https://www.coursera.org/course/progfun
• プログラミングの基礎

言語は OCaml だが関数プログラミングの入門
書としては良書

http://www.amazon.co.jp/dp/4781911609
• Scala関数型デザイン&プログラミング ―
Scalazコントリビューターによる関数型徹底
ガイド

前の 2つよりも難易度は高い。

http://www.amazon.co.jp/dp/4844337769
その他のおすすめ2018版
• (関数プログラミングに限らず)
• 実践Scala入門

https://www.amazon.co.jp/dp/4297101416/
• Scalaをはじめよう! ─マルチパラダイム言語
への招待─

https://nextpublishing.jp/book/9497.html

関数プログラミング ことはじめ (再)