関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1...

164
1 2015.11.13 @kazu_yamamoto

Upload: others

Post on 21-Aug-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

1

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

2015.11.13岡山大学 プログラミング技法 講義資料

山本和彦@kazu_yamamoto

Page 2: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

2

関数プログラミングとは何ですか?

Page 3: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

3

関数プログラミングとは数学でいう「関数」を使ってプログラミングすることです。

Page 4: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

4

「数学の関数」とは何ですか?

Page 5: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

5

引数から値を計算して返す箱です。f(x) = x + 1

Page 6: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

6

数学の関数は、引数が同じであれば必ず同じ値を返します。

f(x) = x + 1 f(1) = 1 + 1 = 2 f(5) = 5 + 1 = 6

Page 7: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

7

C言語にも関数がありますが、これは「数学の関数」ですか?

Page 8: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

8

いいえ。

Page 9: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

9

「C言語の関数」は「数学の関数」よりたくさんのことができてしまいます。

Page 10: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

10

この講義では、数学の関数よりたくさんのことができる箱のことを

「手続き」と呼びます。

Page 11: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

11

また、これ以降単に「関数」と言うと

「数学の関数」のことです。

Page 12: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

12

「C言語の関数」は「手続き」です。

Page 13: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

13

手続きを使ったプログラミングのことを「命令プログラミング」と呼びます。

Page 14: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

14

ここまでのまとめ

関数プログラミング=

数学の関数を使ったプログラミング

命令プログラミング=

手続きを使ったプログラミング

Page 15: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

15

C言語で普通にコードを書くと命令プログラミングをしていることになります。

Page 16: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

16

「手続き」は機能を制限して利用すれば「関数」としても使えます。

Page 17: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

17

C 言語の手続きも数学の関数として利用できるなら、

C言語で関数プログラミングをすることはできますか?

Page 18: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

18

できなくはないですが限られたことしかできません。

C 言語は関数プログラミングをサポートするようには作られてないからです。

Page 19: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

19

数学の関数の具体例をコードで示して下さい。

Page 20: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

20

たとえばC言語で書いた

int f(int x) { return (x + 1); }

は数学の関数です。

Page 21: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

21

たとえばC言語の三角関数sin

も数学の関数です。

Page 22: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

22

しかしC言語のprintf

は数学の関数ではなく手続きです。

「文字を出力する」という数学の関数では

できない仕事をするからです。

Page 23: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

23

また、 int y = 0;

int f(int x) { y = y + 1; return (x + 1); }

も数学の関数ではなく手続きです。グローバル変数 y を書き換えています。それは、数学の関数ではできないことです。

Page 24: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

24

関数の「作用」(仕事)は値を返すことです。

Page 25: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

25

値を返すこと以外の仕事は「副作用」と呼ばれます。

Page 26: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

26

この講義では副作用を持つ箱を

手続きと呼んでいます。

Page 27: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

27

副作用にはどんなものがありますか?

Page 28: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

28

ディスプレイやネットワークへの入出力、グローバル変数の書き換えなどが副作用の例です。

Page 29: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

29

関数プログラミングでは代入がないと聞いたことがあるのですが本当ですか?

Page 30: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

30

代入には「初期化」と

「再代入」があります。

Page 31: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

31

関数プログラミングでは変数を「初期化」できますが

変数に「再代入」することはできません。

Page 32: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

32

たとえば、初期化int x = 1;はできます。

しかし、再代入x = x + 1;はできません。

Page 33: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

33

プログラミングを始めたときx = x + 1;

という文をみてギョッとしましたよね?

Page 34: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

34

関数プログラミングとは、「プログラムの変数」を「数学の変数」として使うという意味もあります。

Page 35: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

35

これまでのまとめ

関数プログラミングとはプログラムの関数を数学の関数

として使ってプログラミングすることプログラムの変数を数学の変数

として使ってプログラミングすること

Page 36: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

36

再代入がないなら for 文さえ書けないのではないですか?

Page 37: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

37

はい、そうです。繰り返しには、再帰を使います。

Page 38: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

38

C言語では階乗のコードを以下のように書けます。

int factorial(int n) { int r = 1; for (int i = 1; i <= n; i++) { r = r * i; } return r; }

関数プログラミングではどのように書くのですか?

Page 39: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

39

階乗の計算はこうですね。

factorial(1) = 1 factorial(2) = 1 * 2 factorial(3) = 1 * 2 * 3 factorial(4) = 1 * 2 * 3 * 4 factorial(5) = 1 * 2 * 3 * 4 * 5

Page 40: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

40

factorial(1) = 1 factorial(2) = 1 * 2 factorial(3) = 1 * 2 * 3 factorial(4) = 1 * 2 * 3 * 4 factorial(5) = 1 * 2 * 3 * 4 * 5

問)それぞれの式を一つ前の式を使って表してください。

Page 41: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

41

factorial(1) = 1 factorial(2) = 1 * 2 factorial(3) = 1 * 2 * 3 factorial(4) = 1 * 2 * 3 * 4 factorial(5) = 1 * 2 * 3 * 4 * 5

は、1つ前の式を使うと、以下のようになります。

factorial(1) = 1 factorial(2) = factorial(1) * 2 factorial(3) = factorial(2) * 3 factorial(4) = factorial(3) * 4 factorial(5) = factorial(4) * 5

Page 42: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

42

factorial(1) = 1 factorial(2) = factorial(1) * 2 factorial(3) = factorial(2) * 3 factorial(4) = factorial(3) * 4 factorial(5) = factorial(4) * 5

問) これを一般化してください。

Page 43: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

43

factorial(1) = 1 factorial(2) = factorial(1) * 2 factorial(3) = factorial(2) * 3 factorial(4) = factorial(3) * 4 factorial(5) = factorial(4) * 5

を一般化すると以下のようになります。 factorial(1) = 1 factorial(n) = factorial(n-1) * n

Page 44: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

44

factorial(1) = 1 factorial(n) = factorial(n-1) * n

をプログラムで書くとどうなりますか?

Page 45: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

45

factorial(1) = 1 factorial(n) = factorial(n-1) * n

を疑似コードで書くと以下のようになります。

int factorial(int n) = if (n == 1) then 1 else factorial(n - 1) * n;

Page 46: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

46

この資料では、関数プログラミングのために

山本が(適当に)作った「疑似コード」を使います。

Page 47: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

47

int factorial(int n) = if (n == 1) then 1 else factorial(n - 1) * n;

波括弧や return がないのはどうしてですか?

Page 48: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

48

命令プログラミングでは複数の文を列挙します。

int main (void) { printf "Hello, "; printf "world!"; printf "\n"; }

セミコロンは文の区切りで、複数の文を一つの固まりにするために

波括弧が使われます。

Page 49: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

49

関数プログラミングでは文ではなく「式」を使ってプログラムを構成します。

int factorial(int n) = if (n == 1) then 1 else factorial(n - 1) * n;

関数定義の右側は「一つの式」なので波括弧は必要ありません。

Page 50: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

50

また関数は必ず値を返します。return を書いていなくても

値を返すのだと理解してください。

int factorial(int n) = if (n == 1) then 1 else factorial(n - 1) * n;

Page 51: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

51

これまでのまとめ

関数プログラミングとは式でプログラムを書くことです。

命令プログラミングとは文でプログラムを書くことです。

Page 52: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

52

int factorial(int n) = if (n == 1) then 1 else factorial(n - 1) * n;

の n は何回も値が変わります。再代入されているのではないですか?

Page 53: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

53

いいえ。あるスコープにおいてn が初期化されると

そのスコープ内では n は不変です。あなたの思っている n は、別々の n と n なのです。

Page 54: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

54

factorial(3) = if (3 == 1) then 1 else factorial(3 - 1) * 3 = factorial(2) * 3 = (if (2 == 1) then 1 else factorial(2 - 1) * 2) * 3 = factorial(1) * 2 * 3 = (if (1 == 1) then 1 else factorial(1 - 1) * 1)

* 2 * 3 = 1 * 2 * 3

Page 55: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

55

関数プログラミングとは不変データプログラミングのことです。

Page 56: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

56

さまざまな不変データがあります。

不変データの代表選手は一方向リストです。

Page 57: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

57

lst1の先頭に要素を追加してもlst1は破壊されません。

Page 58: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

58

なんとなく分かってきましたが、関数プログラミングでは

できることが限られていませんか?

Page 59: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

59

はい。関数プログラミングでできる範囲は命令プログラミングでできる範囲よりも

狭いのです。

Page 60: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

60

ただし、あなたが想像するよりはるかに広い範囲の問題を扱えます。

Page 61: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

61

ディスプレイに表示することが副作用であるなら

関数プログラミングではゲームは作れないのですか?

Page 62: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

62

関数プログラミングだけではゲームは作れません。

通常、関数プログラミングは命令プログラミングと組みわせて使います。

Page 63: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

63

具体的には関数プログラミングで書いた関数は命令プログラミングの手続きから

呼び出して使います。

Page 64: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

64

では逆に関数プログラミングの関数から命令プログラミングの手続きを

呼び出して使ってはいけないのですか?

Page 65: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

65

副作用のない関数から副作用のある手続きを呼び出すと

その関数には副作用があることになり関数ではなく手続きになってしまいます。

せっかくの関数が台無しです。

Page 66: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

66

そこまでして、どうして関数プログラミングを

使うのですか?

Page 67: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

67

関数プログラミングにはたくさんのメリットがあります。

Page 68: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

68

たとえば関数は副作用を持たないのでテストするのが簡単です。

test(factorial(5) == 120);

Page 69: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

69

関数プログラミングではよくテストされた関数を使いより大きな関数を作ります。

このため、大きな関数にもバグが入り込みにくいのです。

Page 70: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

70

また、静的型付き関数型言語を使うとコンパイル時にたくさんのエラーが発見できます。

Page 71: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

71

静的型付き命令型言語でコンパイルする時よりもはるかに多くのエラーを発見できます。

Page 72: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

72

コンパイルが通ればプログラムが正しく動くことも

たくさんあります。

これは静的型付き命令型言語では体験できないことです。

Page 73: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

73

プログラムの開発サイクルで一番コストがかかるのは保守です。

Page 74: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

74

静的型付き関数型言語ではプログラムのどこかを変更する場合変更すべき部分のほとんどを見つけてくれます。

Page 75: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

75

つまり、静的型付き関数型言語では他の手法に比べて

プログラムの保守が容易だと言えます。

Page 76: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

76

もう少し関数プログラミングで問題を解いてみましょう。

Page 77: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

77

10,20,30,40,50を格納する配列またはリストがあります。0から数えて n 番目の要素に n を掛けて

足し合わせなさい。

Page 78: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

78

つまり10*0 + 20*1 + 30*2 + 40*3 + 50*4

という計算をします。

Page 79: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

79

問) C 言語で書いてください。

int calc(int n, int arr[]) { ... }

Page 80: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

80

こんな感じになります。

int calc(int n, int arr[]) { int r = 0; for (int i = 0; i < n; i++) { r = r + arr[i] * i; } return r; }

Page 81: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

81

関数プログラミングではどうやって問題を解くのでしょうか?

Page 82: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

82

関数プログラミングではmap & reduce という考え方でこの問題を解けます。

Page 83: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

83

関数プログラミングの醍醐味を味わうために少し準備をします。

Page 84: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

84

階乗の疑似コードを思い出してください。

int factorial(int n) = if (n == 1) then 1 else factorial(n - 1) * n;

Page 85: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

85

型注釈は書かないことにします。

factorial n = if (n == 1) then 1 else factorial (n - 1) * n;

Page 86: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

86

引数から丸括弧が消えたことにも注意してください。

factorial n = if (n == 1) then 1 else factorial (n - 1) * n;

Page 87: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

87

関数呼び出しのときも丸括弧は不要だとします。

factorial 3; → 6

Page 88: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

88

準備ができました。

Page 89: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

89

今解きたい問題は

10*0 + 20*1 + 30*2 + 40*3 + 50*4

でした。

Page 90: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

90

第一引数にリストの長さ、第二引数にリストを取り計算結果を返す関数を定義します。

calc n lst = ...;

Page 91: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

91

リストのリテラル(直接的な表現)として以下を使います。

[10, 20, 30, 40, 50]

Page 92: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

92

[10, 20, 30, 40, 50]

を図示するとこうなります。

Page 93: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

93

リストの自動生成を使ってカウンタを用意します。

[0 .. 4]; → [0,1,2,3,4]

後で 4 を n - 1 に置き換えます。

Page 94: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

94

リストが2つあると扱いにくいので、2つのリストを1つに閉じ合わせます。

zip [0 .. 4] [10,20,30,40,50]; → [(0,10),(1,20),(2,30),(3,40),(4,50)]

Page 95: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

95

zip は新しいリストを生成します。

Page 96: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

96

(x,y) は「組」(タプル)と呼ばれます。

2つのデータを1つのデータとして扱えます。

Page 97: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

97

[(0,10),(1,20),(2,30),(3,40),(4,50)]

は、「整数と整数の組み」のリストです。

Page 98: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

98

引数として整数と整数の組みを取り掛け算して返す関数を定義します。

mul (i,x) = x * i;

ここで出てくる丸括弧は組みの直接的な表現であることに

注意してください。

Page 99: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

99

今手にしているのは、整数と整数の組みのリスト

[(0,10),(1,20),(2,30),(3,40),(4,50)]

と関数 mul です。各要素を mul に渡してみます。

Page 100: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

100

各要素を mul に渡すにはmap を使います。

map mul [(0,10),(1,20),(2,30),(3,40),(4,50)]; → [0,20,60,120,200]

Page 101: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

101

map も新しいリストを生成します。

Page 102: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

102

リストの要素すべてを足し合わせるには

畳み込み関数 reduce を使います。

reduce + 0 [0,20,60,120,200]; → ((((0 + 0) + 20) + 60) + 120) + 200 → 400

Page 103: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

103

((((0 + 0) + 20) + 60) + 120) + 200

初期値 0 に対してリストの要素を左から+ で畳み込んでいます。

Page 104: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

104

プログラムを完成させます。

calc n lst = reduce + 0 (map mul (zip [0 .. n-1] lst));

Page 105: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

105

ここで |> という演算子を導入します。

(f |> g) x = g (f x)

この定義の意味は分らなくて構いません。

Page 106: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

106

calc n lst = reduce + 0 (map mul (zip [0 .. n-1] lst));

は |> を使うと、以下のようになります。

calc n = zip [0 .. n-1] |> map mul |> reduce + 0 ;

Page 107: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

107

calc n = zip [0 .. n-1] |> map mul |> reduce + 0 ;

はシェルでのパイププログラミングのように見えませんか?

Page 108: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

108

関数プログラミングとはパイププログラミングのようなプログラミングのことです。

Page 109: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

109

図示してみましょう。

Page 110: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

110

UNIX の哲学

"Do one thing and do it well"

パイプの作者 Doug McIlroy

Page 111: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

111

関数プログラミングでは、一つのことをうまくやれるように

関数を作ります。

Page 112: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

112

関数プログラミングではバグがないと確信できる関数を

組み合わせて新しい関数を作ります。

Page 113: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

113

関数プログラミングとは部品プログラミングです。

Page 114: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

114

calc n = zip [0 .. n-1] |> map mul |> reduce + 0 ;

zip の引数は2つのはずです。

zip [0 .. n-1] は、引数が足りなくないですか?

Page 115: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

115

関数型言語には「部分適用」という機能があり

引数の一部だけを関数に渡せます。

Page 116: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

116

ここでまた準備をします。新しい型注釈を導入します。

Page 117: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

117

新しい型注釈は本文とは別の行に書きます。

factorial : int -> int; factorial n = if (n == 1) then 1 else factorial (n - 1) * n;

Page 118: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

118

一番右側が返り値の型それ以外は引数の型です。

factorial : int -> int;

Page 119: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

119

引数が複数ある例を示します。

count : char -> string -> int;

第一引数の型が文字、第二引数の型が文字列、返り値の型が整数という意味です。

Page 120: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

120

count : char -> string -> int;

は、指定された文字が文字列の中に何個あるか数える関数だとします。

count ’a’ "banana"; → 3

Page 121: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

121

準備ができました。

部分適用の動作原理を考えましょう。

Page 122: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

122

count の型注釈に丸括弧を補ってみます。

count : char -> (string -> int);

Page 123: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

123

count : char -> (string -> int);

は、第一引数が文字でstring -> int を返す関数だと解釈できます。

Page 124: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

124

string -> int

とは何か考えてください。

Page 125: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

125

string -> int

は、第一引数の型が文字列で返り値の型が整数である関数の型です。

Page 126: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

126

count : char -> (string -> int);

つまり count は、引数として文字を取り関数を返す関数です。

Page 127: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

127

count に例えば ’a’ を部分適用した関数 count_a を作ってみましょう。

count_a : string -> int; count_a = count ’a’;

count_a "banana"; → 3

Page 128: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

128

このように部分適用とは一部の引数を固定して

新たに関数を作ることです。

部分適用によって汎用の部品から専用の部品を作り出せます。

Page 129: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

129

今は、zip [0 .. n-1] が何かを考えているのでした。

calc n = zip [0 .. n-1] |> map mul |> reduce + 0 ;

Page 130: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

130

zip の型は、以下の通りです。

zip : [a] -> [b] -> [(a,b)];

Page 131: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

131

a や b は型変数と呼ばれます。

Page 132: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

132

型注釈に型変数を持つ関数は、引数の型に依存せずに仕事をします。

Page 133: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

133

実際に利用するときには、型変数が何かの型に固定されます。

たとえば、a が int に固定されたり

b が char に固定されたりします。

Page 134: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

134

[int] と書くと要素の型が int である

リストの型という意味だとします。

Page 135: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

135

[a] は、要素が何かの型を持つリストの型だという意味です。

Page 136: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

136

リストの中の要素は、すべて同じ型を持っていないと

いけません。

Page 137: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

137

[1,2,3,4] は OK ですが、

[1,’a’,3.1,"hello"] は NG です。

Page 138: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

138

zip [0 .. n-1]の型を考えてみましょう。

Page 139: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

139

zip [0 .. n-1]

とzip : [a] -> [b] -> [(a,b)];

を見比べるとa は int だと分ります。

Page 140: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

140

よって以下のようになります。

zip [0 .. n - 1] : [b] -> [(int,b)];

引数の数が減ることにも注意しましょう。

zip : [a] -> [b] -> [(a,b)];

Page 141: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

141

次に map mul の型を考えましょう。

Page 142: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

142

map の型は以下の通りです。map : (a -> b) -> [a] -> [b];

Page 143: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

143

mul の型は以下の通りです。

mul : (int,int) -> int; mul (i,x) = x * i;

(int,int) は整数と整数の組みという意味です。

Page 144: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

144

mul : (int,int) -> int;map : (a -> b) -> [a] -> [b];

から map mul の型が以下のように推論できます。

map mul : [(int,int)] -> [int];

Page 145: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

145

同様に + : int -> int -> int; reduce : (b -> a -> b) -> b -> [a] -> b;

から、reduce + 0 の型は以下のように推論できます。

reduce + 0 : [int] -> int;

Page 146: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

146

zip [0 .. n - 1] : [b] -> [(int,b)]; map mul : [(int,int)] -> [int]; reduce + 0 : [int] -> int;

から、b は int だと推論できます。

(本当は |> の型も必要です)

Page 147: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

147

図示してみましょう。

Page 148: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

148

1つ前の返り値の型と次の引数の型が

同じであることが分かります。

Page 149: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

149

factorial の型を推論してみましょう。

factorial n = if (n == 1) then 1 else factorial (n - 1) * n;

Page 150: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

150

引数は1つですから、? -> ?

の形をしているはずです。

factorial : ? -> ?; factorial n = if (n == 1) then 1 else factorial (n - 1) * n;

Page 151: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

151

n == 1 から第一引数は int だと

分ります。 factorial : int -> ?; factorial n = if (n == 1) then 1 else factorial (n - 1) * n;

Page 152: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

152

1 を返しているので、返り値の型は int だと分ります。

factorial : int -> int; factorial n = if (n == 1) then 1 else factorial (n - 1) * n;

Page 153: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

153

これまで factorial がどのように作られているかから型を推論しました。

Page 154: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

154

factorial は再帰で定義されているので使われてもいます。

factorial : int -> int; factorial n = if (n == 1) then 1 else factorial (n - 1) * n;

Page 155: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

155

factorial (n - 1) * n;

の部分では、型は合っているでしょうか?

Page 156: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

156

* : int -> int -> int;ですから factorial の返り値の型は

int のはずです。

n - 1 : int;ですから、factorial の引数の型は

int のはずです。

つまり、こうです。factorial : int -> int;

Page 157: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

157

どう組み立てられているかからfactorial : int -> int;

どう使われているかからfactorial : int -> int;

のように双方向の型推論をして型に間違いがないことが確かめらました。

Page 158: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

158

プログラムを式で組み立てるとあらゆる部分式で

内側から組み立てた型と外側から利用する型とで

双方向の型検査を実施できます。

Page 159: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

159

これが静的型付き関数型言語を使うと

たくさんエラーを発見できる理由です。

Page 160: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

160

型注釈は必ずしも書く必要がありません。

コンパイラーが推論するので、推論結果を自動的に挿入できます。

Page 161: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

161

難しい関数を書く場合は先に型注釈を書いて

型のレベルで設計することもあります。

その場合、型が実装を導いてくれることがたびたびあります。

Page 162: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

162

そういう体験をすれば関数プログラミングの

真髄に触れたことになります。

Page 163: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

163

頑張ってください。

Page 164: 関数プログラミング ことはじめkazu/material/2015-fp.pdf · 1 関数プログラミング ことはじめ 2015.11.13 岡山大学 プログラミング技法 講義資料

164

関数プログラミングに興味を持った人は

以下の記事を読むとよいでしょう。

「入門 関数プログラミング」http://gihyo.jp/dev/feature/01/functional-prog