F#によるFunctional Programming入門

6,983 views

Published on

高専カンファ in 三重2の発表資料です。高専生向けなので、入門と言いつつアレな感じですので注意してください。

Published in: Technology

F#によるFunctional Programming入門

  1. 1. F#による Functional Programming 入門 bleis-tift December 22 2012
  2. 2. 自己紹介id:bleis-tift / @bleis鈴鹿高専電子情報工学科卒業生Microsoft MVP for Visual F#
  3. 3. いきなりですが質問ですQ1. プログラミングしたことありますか?
  4. 4. Q2. どんな言語を使いましたか?C, C++, BASIC, Java, C#, PHP, Perl, Python,Ruby, JavaScript, ...授業だと大体こんなところでしょうか?
  5. 5. 今日話すこと授業ではあまり取り上げられないであろうタイプの言語である、 関数型プログラミング言語を取り上げ、関数プログラミングについて紹介します。
  6. 6. その前に・ ・・手続型言語ってなんでしょうか? 手続きを記述する 処理のまとまりを「手続き」としてまとめる 手続きを構成するのは「文」が基本 状態を書き換える 基本的に変数は書き換え可能 単純なループは変数の書き換えがほぼ必須 手続型言語は文中心かつ、 書き換え可能な変数が必須
  7. 7. 手続型言語に対し・ ・・関数型言語はざっくりこんな感じです。 関数を記述する 処理のまとまりを「関数」としてまとめる 関数を構成するのは「式」 状態の書き換えは局所化する 基本的に変数は書き換え不可能 変数の書き換えによらない繰り返し 関数型言語は式中心
  8. 8. 式と文の違い式と文の違い、分かりますか? 式は評価することで値になる 文そのものは値を持たない値が書けるところには式が書ける!
  9. 9. 式と文の違い (文)Javaint[] f(int x) { int[] res = new int[x]; for (int i = 0; i < x; i++) res[i] = i * 2; return res;}int sum(int[] xs) { ... }// これはもちろん可能int total = sum(f(10));// これは出来ないint total = sum( int[] res = new int[x]; for (int i = 0; i < x; i++) res[i] = i * 2; return res;);
  10. 10. 式と文の違い (式)F#let f x = let res = Array.zeroCreate x for i in 0..(x - 1) do res.[i] <- i * 2 reslet sum xs = ...// これはもちろん可能let total = sum(f 10)// これも可能!let total = sum ( let res = Array.zeroCreate 10 for i in 0..(10 - 1) do res.[i] <- i * 2 res )
  11. 11. 式中心であることの利点式を組み合わせてより大きく 複雑な式を構築できる!
  12. 12. F#についてちょっと休憩して、F#について。 Visual Studio に標準搭載されている、 マルチパラダイム言語 (コアとなるパラダイムは関数型) .NET Framework 上で動作するため、 .NET の資産を活かせる 強い静的型付き言語 インデントを文法に含む (軽量構文の場合) オープンソース
  13. 13. さっきのコードをもう一回見てみるlet f x = let res = Array.zeroCreate x for i in 0..(x - 1) do res.[i] <- i * 2 // 状態書き換えてる! res状態書き換えない版let f x = Array.init x ((*)2)ループを自分で書く必要すらない!→バグの抑制につながる
  14. 14. F#って静的型付き言語って言ったよね・ ? ・・今までのコード、型書いてません。でも、型推論によってコンパイル時に型が付いていたのです!// int を受け取って int[] を返す関数// 「int -> int[]」と記述するlet f x = Array.init x ((*)2)この関数にどうやって型が付いたのか見ていきます。
  15. 15. その前に((*)2)って何!
  16. 16. ((*)2) についてF#では、中置演算子を関数として扱える2 * 4 // 8(*) 2 4 // 8そして、複数引数の関数に引数を「一部」与えることができるlet f x y = x * yf 2 4 // 8let g = f 2g 4 // 8つまり、((*)2) は「何かを 2 倍する関数」
  17. 17. 型推論のステップ let f x = Array.init x ((*)2) 1 Array.init は、int → (int → α) → α []. 2 x は Array.init の第一引数として渡している x の型は int 3 戻り値の型は Array.init の戻り値の型と同じ. f の戻り値の型はα [] 4 掛け算「*」の型は int → int → int ((*)2) は引数を一つ与えているので、int → int これを Array.init の第二引数として渡しているの. で、(int → α) のαは int 全体として、f は「int → int ] [ 」
  18. 18. 型推論の利点 型を書かなくても型が付く→型チェックの恩恵が受けられる
  19. 19. ちょっと休憩ここまでで何か質問ありますか?
  20. 20. ここからは型についてA か B か C のどれかを表す型が欲しくなったときどうしますか?列挙型を使う?Java の例enum T { A, B, C }これでどうや!
  21. 21. 列挙型でダメな場合列挙型は「A の時のみ必要な情報」を保持できない例えば、 アンケートの項目「このイベントを何で知り ましたか?」 Web サイト / Twitter / Facebook / その他 その他の場合は理由が必要これは列挙型ではうまくあらわせない・ ・・
  22. 22. どうしよう列挙型とクラスでやる?enum InfoSource { WebSite, Twitter, Facebook, Etc}class Answer { InfoSource source; String etc; ...}Etc 以外の時に etc が使われてしまうかもしれない
  23. 23. どうしよう・ ・・クラス階層を作ってみる?こうや!interface InfoSource { }enum WebSite implements InfoSource { Instance }enum Twitter implements InfoSource { Instance }enum Facebook implements InfoSource { Instance }class Etc implements InfoSource { String etc;} Etc 以外の時は etc が使えない! でも InfoSource を Etc として使うためには キャストが必要 結局コンパイル時に解決できていない そこで Visitor ですよ!
  24. 24. VisitorVisitor の導入interface Visitor { void webSite(WebSite w); void twitter(Twitter t); void facebook(Facebook f); void etc(Etc e);}interface InfoSource { void accept(Visitor v); }enum WebSite implements InfoSource { Instance; public void accept(Visitor v) { return v.webSite(this); }}enum Twitter implements InfoSource { Instance; public void accept(Visitor v) { return v.twitter(this); }}enum Facebook implements InfoSource { Instance; public void accept(Visitor v) { return v.facebook(this); }}class Etc implements InfoSource { String etc; public void accept(Visitor v) { return v.etc(this); }}
  25. 25. やってられるかー!
  26. 26. そこで直和型ですよ!F#の直和型 (判別共用体) を使えばこの通りtype InfoSource = | WebSite | Twitter | Facebook | Etc of stringすっきり!
  27. 27. 使い方も簡単match 式を使うlet toStr src = match src with | WebSite -> "Web サイト" | Twitter -> "Twitter" | Facebook -> "Facebook" | Etc etc -> "その他 (" + etc + ")"function 式でも可let toStr = function| WebSite -> "Web サイト"| Twitter -> "Twitter"| Facebook -> "Facebook"| Etc etc -> "その他 (" + etc + ")"
  28. 28. 判別共用体のさらなる用途ここまでは、列挙型に毛の生えたようなものここまででも便利だが・ ・・判別共用体は、自分自身を参照することで更に便利に!
  29. 29. 情報系ではおそらく一度は実装する二分木これを実装してみましょう
  30. 30. 二分木の実装Java の場合public class BinTree { final int value; BinTree left = null; BinTree right = null; public BinTree(int value) { this.value = value; }}F#の場合type BinTree = | Leaf | Node of BinTree * int * BinTree // ↑ ↑
  31. 31. 二分探索木の探索Java の場合public boolean contains(int value) { if (value == this.value) return true; if (value < this.value && lelft != null) return left.contains(value); if (this.value < value && right != null) return right.contains(value); return false;}F#の場合let rec contains value = function| Leaf -> false| Node (l, v, r) when v = value -> true| Node (l, v, r) when value < v -> contains value l| Node (l, v, r) when v < value -> contains value r
  32. 32. 両者の違いF#の実装の方が短い (Java よりも宣言的)Java 版は null を使うため、NullPointerException が起こりうるのに対し、F#版は null を使わないJava 版は二分木の操作は参照をやりくりする必要があるのに対し、F#版はパターンマッチが使える
  33. 33. 判別共用体は再帰的なデータ構造を上手に扱える 再帰的なデータ構造は思いのほか多い リスト、木、ファイルシステム、などなど簡単に記述できる 型を定義するハードルが低い (型が軽い)
  34. 34. 取りこぼしリストとリストの処理高階関数 (関数が第一級の値)少ないルールを組み合わせるモナドとか
  35. 35. まとめ文より式!直和型 (判別共用体)!F# ! F# !

×