たのしい関数型

94
ー関数型言語入門 たのしい関数型 怖くないよ 怖くないよ

Upload: shinichi-kozake

Post on 27-May-2015

5.303 views

Category:

Technology


2 download

DESCRIPTION

第1回 関数型言語勉強会 大阪の資料です。

TRANSCRIPT

Page 1: たのしい関数型

ー関数型言語入門

たのしい関数型

怖くないよ怖くないよ

Page 2: たのしい関数型

本セッションは関数型入門ですたのしく関数型を学ぶことを目的としております。

目的本勉強会の趣旨

Page 3: たのしい関数型

つまりですね

Page 4: たのしい関数型

本セッションは関数型入門ですたのしく関数型を学ぶことを目的としております。

目的本勉強会の趣旨 -ATENDより

Page 5: たのしい関数型

大事なことなので2回いいました

Page 6: たのしい関数型

注意事項

Page 7: たのしい関数型

Monad圏論

α-変換、β-簡約がどうとか

ストリクト解析がどうとか

関数型には難しい概念もありますが・・

注意事項

Page 8: たのしい関数型

Monad圏論

ストリクト解析がどうとか

当セッションの範囲外です

注意事項

α-変換、β-簡約がどうとか

Page 9: たのしい関数型

関数型には怖い人がいるらしいですが・・特に東方

注意事項

Page 10: たのしい関数型

会場を間違えていませんか?

注意事項

Page 11: たのしい関数型

注意事項

THIS IS スパルタ!!

Page 12: たのしい関数型

注意事項

ないです

Page 13: たのしい関数型

たのしく学びましょう~

Page 14: たのしい関数型

Status

小酒 信一システムアーキテクト

せいべつ : おとこ

Twitter : s_kozake

所属 : SIer

Java : 195

Scala : 30

Hakell : 35

協調性 : 2

さいだいHP : 24

さいだいMP : 1

ステータス : 緊張

GOLD : 0E 結婚指輪

E GATEWAY ノートPC

レベル : 0x24

Page 15: たのしい関数型

関数型とは?

Page 16: たのしい関数型

関数型とは?副作用を持たない関数を中心にしてプログラムを組み立てる手法

Page 17: たのしい関数型

関数型とは?副作用を持たない関数を中心にしてプログラムを組み立てる手法

副作用を持たない関数とは?

同じ関数をなんど呼び出しても。同じ引数についていつも同じ結果

「今回」f(x) = a なら、「次回」もf(x)=a

Page 18: たのしい関数型

関数型とは?副作用を持たない関数を中心にしてプログラムを組み立てる手法

副作用を持たない関数とは?

同じ関数をなんど呼び出しても。同じ引数についていつも同じ結果

「今回」f(x) = a なら、「次回」もf(x)=a

等式推論

=

Page 19: たのしい関数型

具体例JavaでListを扱うコード

List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3);

System.out.println(list.get(0)); // 1 list.set(0, 4); System.out.println(list.get(0)); // 4

Page 20: たのしい関数型

具体例JavaでListを扱うコード

List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3);

System.out.println(list.get(0)); // 1 list.set(0, 4); System.out.println(list.get(0)); // 4

副作用副作用

Listの内部状態を変更している

Page 21: たのしい関数型

具体例ScalaでListを扱うコード

val list = List(1, 2, 3)

println(list(0)) // 1 val list2 = list.updated(0, 4) println(list(0)) // 1 println(list2(0)) // 4

Page 22: たのしい関数型

具体例ScalaでListを扱うコード

val list = List(1, 2, 3)

println(list.apply(0)) // 1 val list2 = list.updated(0, 4) println(list.apply(0)) // 1 println(list2.apply(0)) // 4

新しいListを作成している

Page 23: たのしい関数型

具体例JavaのStringクラス

final String str = "abc"; System.out.println(str); // "abc" final String str2 = str.substring(1); System.out.println(str); // "abc" System.out.println(str2); // "bc"

JavaのStringクラスは不変

Page 24: たのしい関数型

なぜ関数型?

Page 25: たのしい関数型

なぜ関数型?CPUのマルチコア化

・10年後には、コア1000!?・今まで以上に平行プログラミングが重要・副作用のない関数型は並行処理と相性がいい

Page 26: たのしい関数型

なぜ関数型?CPUのマルチコア化

・10年後には、コア1000!?・今まで以上に平行プログラミングが重要・副作用のない関数型は並行処理と相性がいい

Java8にラムダが導入されるのも、CPUコア数増加に対応し、並行処理を効率よく扱うため

Page 27: たのしい関数型

なぜ関数型?高階関数

・関数がファーストクラスオブジェクト・コードパターンを再利用しやすい・リストのライブラリが使いやすい

・map・fiter・foldl / foldr・find・etc..

Page 28: たのしい関数型

なぜ関数型?内部DSL

・Domain Specific Language、領域特化言語・高階関数や遅延評価をもつ関数型は 内部DSLを作成しやすい。 「ホスト言語」として有力候補

Page 29: たのしい関数型

なぜ関数型?・「HOW」から「WHAT」へ

qsort [] = []qsort(x:xs) = qsort l ++ [x] ++ qsort r where l = [a | a <- xs, a < x] r = [a | a <- xs, a >= x]

・クイックソートのコード例

ハードウェア性能の向上

手続き型 手続き型 手続き型

オブジェクト指向型

関数型

オブジェクト指向型

Page 30: たのしい関数型

なぜ関数型?ハッカーになれる!?

LISP は、それをモノにしたときのすばらしい悟り体験のために勉強しましょう。この体験は、その後の人生でよりよいプログラマーとなる手助けとなるはずです。たとえ、実際には LISP そのものをあまり使わなくても。

~ハッカーになろう~より抜粋http://cruel.org/freeware/hacker.html

Page 31: たのしい関数型

リストとは?

Page 32: たのしい関数型

リストとは?・同じ型の要素の並び 関数型言語として最も代表的なデータ構造

・神は言われた。「リストあれ」 リストを処理するのが関数型言語の プログラミングというほど、関連が深い

・単一方向の線型リスト

head tail head tail []

1 2

Page 33: たのしい関数型

リストとは?コード例(HaskellのREPLでの例)

Page 34: たのしい関数型

リストとは?コード例(HaskellのREPLでの例)

1 2 3

xs []

ys0

zs

Page 35: たのしい関数型

リストとは?文字列もリストとして扱われる

Page 36: たのしい関数型

タプルとは?

Page 37: たのしい関数型

タプルとは?・有限個の要素の組。 各要素の型は異なってもいい

・要するに、構造体

・要素数0のタプルはユニットと呼ばれる 要素数2のタプルは組、3は三つ組

Page 38: たのしい関数型

タプルとは?人間関係で考えると

組 二股 三つ組

Page 39: たのしい関数型

タプルとは?

Page 40: たのしい関数型

リスト内包表記とは?

Page 41: たのしい関数型

リスト内包表記とは?・数学の内包表記は、既存の集合から 新しい集合を生成する。

{ X | X ∈ {1..5}} = {1, 4, 9, 16, 25}2

・既存のリストから新しいリストを生成する のがリスト内包表記

Page 42: たのしい関数型

リスト内包表記とは?

[ X | X ← [1..5]]2

生成器

Page 43: たのしい関数型

リスト内包表記とは?

[ X×Y | X ← [1..3],Y←[1..3]]

複数の生成器も列挙できる。

生成器 生成器

Page 44: たのしい関数型

リスト内包表記とは?Javaで書くと、こんなイメージ

List<Integer> xs = new ArrayList<Integer>();xs.add(1); xs.add(2); xs.add(3);

List<Integer> ys = new ArrayList<Integer>();ys.add(1); ys.add(2); ys.add(3);

List<Integer> ret = new ArrayList<Integer>();

for (int x : xs) { for (int y : ys) { ret.add(x * y); }} System.out.println(ret);

Page 45: たのしい関数型

リスト内包表記とは?

[ X | X ← [1..10], X `mod` 2 =0]

ガードと呼ばれる論理式も使用できる

ガード生成器

Page 46: たのしい関数型

リスト内包表記とは?

[ X×Y | X ← [1..4], even X, Y←[1..4], oddY]

複数の生成器とガードの組み合わせ

[(0×1), (0×3),(2×1), (2×3),(4×1), (4×3)]

生成器 ガード 生成器 ガード

Page 47: たのしい関数型

リスト内包表記とは?Javaで書くと、こんなイメージ

for (int x : xs) { if (x % 2 == 0) { for (int y : ys) { if (y % 2 == 1) { ret.add(x * y); } } }} System.out.println(ret);

Page 48: たのしい関数型

リスト内包表記とは?Scalaでは、for式をリスト内包表記として使える

scala> for (x <- 0 to 4 if x % 2 == 0; y <- 0 to 4 if y % 2 == 1) yield x * yres3: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 0, 2, 6, 4, 12)

Page 49: たのしい関数型

リスト内包表記とは?

zip [1,2,3] [4,5]

関数zip

2つのリストをとり、対応する要素を組として1つのリストをつくる関数

短いリストにあわせられる短いリストにあわせられる

Page 50: たのしい関数型

リスト内包表記とは?zipを人間関係で考えると

余りは切り捨てられます余りは切り捨てられます

ListW

ListM

Page 51: たのしい関数型

再帰関数とは?

Page 52: たのしい関数型

再帰関数とは?・関数自身を使って定義された関数のこと

・関数型言語では、ループを実現する仕組み として再帰が使われる。

qsort [] = []qsort(x:xs) = qsort l ++ [x] ++ qsort r where l = [a | a <- xs, a < x] r = [a | a <- xs, a >= x]

・クイックソートのコード例

Page 53: たのしい関数型

再帰関数とは?

Nまで階乗を求める関数 func

0 × 1 × 2 ×... N

副作用を伴うループを使ったケースと副作用を伴わない再帰を使ったケースで書いてみる。

Page 54: たのしい関数型

再帰関数とは?副作用を伴うループを使ったケース

def fact(n :Int):BigInt = { var ret = BigInt(1) for (i <- 1 to n) { ret = ret * i } ret }

Page 55: たのしい関数型

再帰関数とは?副作用を伴わない再帰を使ったケース

def fact(n :Int):BigInt = { if (n == 0) { 1 } else { n * fact(n - 1) } }

Page 56: たのしい関数型

再帰関数とは?副作用を伴わない再帰を使ったケース

def fact(n :Int):BigInt = { if (n == 0) { 1 } else { n * fact(n - 1) } }

0の階乗は1nの階乗は n + (n-1)の階乗

<=基底部<=再帰部

Page 57: たのしい関数型

再帰関数とは?

再帰関数の考え方

1. 型を定義する

factはInt型のnを受け取り、BigInt型を返す

def fact(n:Int):BigInt

2. 場合分けをする

3. 簡単な方を定義する

4. 複雑な方を定義する

Page 58: たのしい関数型

再帰関数とは?

再帰関数の考え方

1. 型を定義する

2. 場合分けをする

3. 簡単な方を定義する

4. 複雑な方を定義する

nが0の場合nが0以外の場合

Page 59: たのしい関数型

再帰関数とは?

再帰関数の考え方

1. 型を定義する

2. 場合分けをする

3. 簡単な方を定義する

4. 複雑な方を定義する

0の階乗は1

Page 60: たのしい関数型

再帰関数とは?

再帰関数の考え方

1. 型を定義する

2. 場合分けをする

3. 簡単な方を定義する

4. 複雑な方を定義する

nの階乗は n × (n-1)の階乗

Page 61: たのしい関数型

再帰関数とは?再帰の問題点

> scala Fact1 10000284625968091705451890641321211986889...>> scala Fact2 10000java.lang.StackOverflowError :

Page 62: たのしい関数型

再帰関数とは?再帰の問題点

fact(3)= 3 × fact(2)= 3 × 2 × fact(1)= 3 × 2 × 1 × fact(0)= 3 × 2 × 1 × 0

スタックの使いすぎ

> scala Fact1 10000284625968091705451890641321211986889...>> scala Fact2 10000java.lang.StackOverflowError :

Page 63: たのしい関数型

再帰関数とは?StackOverflowErrorが発生しないfact定義

def fact(n: Int):BigInt = { def factorial(n: Int, acc: BigInt):BigInt = { if (n == 0) { acc } else { factorial(n - 1, n * acc) } } factorial(n, 1) }

Page 64: たのしい関数型

再帰関数とは? def fact(n: Int):BigInt = { def factorial(n: Int, acc: BigInt):BigInt = { if (n == 0) { acc } else { factorial(n - 1, n * acc) } } factorial(n, 1) }

fact(3) = factorial(3, 1) = factorial(3 - 1, 3 * 1) = factorial(2 - 1, 2 * 3) = factorial(1 - 1, 3 * 6) = 24

スタックを消費しない

Page 65: たのしい関数型

再帰関数とは? def fact(n: Int):BigInt = { def factorial(n: Int, acc: BigInt):BigInt = { if (n == 0) { acc } else { factorial(n - 1, n * acc) } } factorial(n, 1) }

fact(3) = factorial(3, 1) = factorial(3 - 1, 3 * 1) = factorial(2 - 1, 2 * 3) = factorial(1 - 1, 1 * 6) = 6

スタックを消費しない

Page 66: たのしい関数型

再帰関数とは?

末尾再帰とは

一番最後に自分自身を再帰的に呼び出している関数

末尾再帰関数の結果値を持ち運ぶ引数accはアキュムレータと呼ばれる

ちなみに、Java8よりJavaにも末尾再帰が導入されるらしい

Page 67: たのしい関数型

ラムダとは?

だっちゃ

Page 68: たのしい関数型

ラムダとは?

引数のパターン、および引数から結果を計算する本体からなる、関数名を含まない式。

Page 69: たのしい関数型

ラムダとは?

要は無名関数

引数のパターン、および引数から結果を計算する本体からなる、関数名を含まない式。

Page 70: たのしい関数型

ラムダとは?

要は無名関数

こんな感じ=>

λx.x+x

引数のパターン 結果を計算する本体

引数のパターン、および引数から結果を計算する本体からなる、関数名を含まない式。

Page 71: たのしい関数型

ラムダとは?こんな感じに使える

>(λx.x+x) 24

Page 72: たのしい関数型

ラムダとは?例えば、add関数

add x y = x+y

add = λxy.x+y

このようにも表現できる

Page 73: たのしい関数型

ラムダとは?JavaScriptと比較すると分かりやすい。(厳密にはちがいますが。。)

add = λxy.x+y

varadd = function(x,y){ return x+y; }

Page 74: たのしい関数型

ラムダとは?

> let f = \n -> n * n> :type ff :: Integer -> Integer> f(10)100

Haskell

scala> val f = (n:Int) => n * nf: Int => Int = <function1>

scala> f(10)res0: Int = 100

Scala

Page 75: たのしい関数型

ラムダとは?

public static interface Func<T, R> { public R eval(T p); } public static void main(String[] args) { Func<Integer, Integer> f = (Integer n) -> n * n; System.out.println(f.eval(10)); }

Java8

> java Lambda100

Page 76: たのしい関数型

ラムダとは?

public static interface Func<T, R> { public R eval(T p); } public static void main(String[] args) { Func<Integer, Integer> f = (Integer n) -> n * n; System.out.println(f.eval(10)); }

Java8

> java Lambda100

SAM(Single Abstract Method) typeSAM(Single Abstract Method) type

インターフェース実装のシンタックスシュガー

インターフェース実装のシンタックスシュガー

Page 77: たのしい関数型

高階関数とは? 高い意識で書く

それが俺の

   高階関数だ

Page 78: たのしい関数型

高階関数とは?

引数として関数を取ったり、返り値として関数を返したりする関数

高階関数を利用したライブラリ関数が超便利!

・map・fiter・foldl / foldr・find・etc..

Page 79: たのしい関数型

高階関数とは?好きな関数:if

そうだ!if式を作ろう

( ゚∀゚)o彡°

Page 80: たのしい関数型

高階関数とは?

myIf :: Bool -> a -> a -> amyIf cond t f = case cond of True -> t False -> f

Haskell

実行結果

> let a = myIf (1==1) (\n -> n+n) (\n -> n*n)> :type aa :: Integer -> Integer> a 1020

Page 81: たのしい関数型

高階関数とは?

def myIf[T] (cond: Boolean, t: => T, f: => T):T = cond match { case true => t case false => f} val a = myIf(1 == 1, (n:Int) => n + n, (n:Int) => n * n)println(a(10))

Scala

実行結果

> scala MyIf20

Page 82: たのしい関数型

高階関数とは?

public static <T, R> Func<T, R> myIf(boolean cond, Func<T, R> t, Func<T, R> f) { return cond ? t : f;} public static void main(String[] args) { Func<Integer, Integer> a = myIf(1==1,(Integer n) -> n + n, (Integer n) -> n * n); System.out.println(a.eval(10));}

Java8

実行結果> java MyIf20

Page 83: たのしい関数型

クロージャーとは?

Page 84: たのしい関数型

クロージャーとは?Scalaobject Closure extends App { def inc(n:Int): () => Int = { var m = n () => { m = m + 1; m } }

val f1 = inc(1) val f2 = inc(10)

println( f1() ); println( f1() );

println( f2() ); println( f2() );}

実行結果>scala Closure231112

Page 85: たのしい関数型

クロージャーとは?

クロージャーとは関数の一種

引数以外の変数を、自身が定義された環境において解決する仕組み

関数とメモリ空間のセット関数にメモリ空間がくっついているイメージ

Page 86: たのしい関数型

カリー化とは?カリー カリー

Page 87: たのしい関数型

カリー化とは?

カレーじゃないよ。論理学者 Haskell Curry さんの名前からつけられた。

関数は関数を返り値として返せる性質を活かし、二つ以上の引数を持つ関数を、一度に一つの引数を取る関数として定義することをカリー化と表現する。

Page 88: たのしい関数型

カリー化とは?x と y の和を求めるadd関数

add は1つの数値をとり、数値をとって数値を返す関数を返す

a -> (a ->a)

Page 89: たのしい関数型

カリー化とは?x と y の和を求めるadd関数

部分適用 という

Page 90: たのしい関数型

カリー化とは?

scala> def add(x:Int)(y:Int) = x + yadd: (x: Int)(y: Int)Int

scala> val addOne = add(1)_addOne: Int => Int = <function1>

scala> addOne(10)res0: Int = 11

Scala

Page 91: たのしい関数型

How to Study?

Page 92: たのしい関数型

How to Study?おすすめ本(Haskell)

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

Page 93: たのしい関数型

How to Study?おすすめ本(Scala)

Scala 第2版(コップ本) Scala実践プログラミングの3章

Page 94: たのしい関数型

ご清聴ありがとうございました!