Upgrade to Pro — share decks privately, control downloads, hide ads and more …

関数型プログラミングを わかった気になってみる

関数型プログラミングを わかった気になってみる

関数型プログラミングは、手続き型プログラミングに慣れている人にとって非常にとっつきづらい印象で、学習開始にあたっての心理的な障害がとても大きいとおもいますが、それを取り除くべく、超乱暴な説明をしてみました。

aremokoremo

May 03, 2016
Tweet

Other Decks in Programming

Transcript

  1. 本資料の目的とか • 目的 • 関数型プログラミングについて、なんとなくわかった気にな れる • 対象 • 関数型プログレミングアレルギーな人

    • Object志向プログラミング、手続き型プログラミングをそ れなりにやってきたが、関数型プログラミングに手を出そ うと思ったら、文法からなにから意味不明すぎて脳死状態 に陥ってる人(=主にわたしです)
  2. さて、気分も悪くなった所で罵倒ロジックをプログラム化してみよう Start Loop(人数分) Lambda 理解してる? End Lambda 理解したい? printIn("老害乙") Yes

    Yes No No for(Engineer one : engineers){ if (false == one.understandLambda()){ if (false == one.hopeUnderstandingLambda()){ System.out.println(one.name + "老害乙"); } } } (え?何の話だよ、と思うでしょうけどここは我慢) 別に難しく無い。 命令型、手続き型プログラム。 If/else/forで分岐、ループ作りつつ、 実行すべき処理を、サブルーチン使ったりしつつ、 逐次実行。
  3. 寛大な気持ちで、もう一度、罵倒ロジックをプログラム化してみよう Start Loop(人数分) Stream 理解してる? End Stream 理解したい? printIn("帰れ") Yes

    Yes No No for(Engineer one : engineers){ if (false == one.understandStream()){ if (false == one.hopeUnderstandingStream()){ System.out.println(one.name + “帰れ”); } } }
  4. Start Loop(人数分) Stream 理解してる? End Stream 理解したい? printIn("帰れ") Yes Yes

    No No for(Engineer one : engineers){ if (false == one.understandStream()){ if (false == one.hopeUnderstandingStream()){ System.out.println(one.name + “帰れ”); } } } あれ? どこかで見た形だな。 for(Engineer one : engineers){ if (false == one.understandLambda()){ if (false == one.hopeUnderstandingLambda()){ System.out.println(one.name + "老害乙"); } } } 寛大な気持ちで、もう一度、罵倒ロジックをプログラム化してみよう
  5. Start Loop(人数分) Stream 理解してる? End Stream 理解したい? printIn("帰れ") Yes Yes

    No No for(Engineer one : engineers){ if (false == one.understandStream()){ if (false == one.hopeUnderstandingStream()){ System.out.println(one.name + “帰れ”); } } } あれ? どこかで見た形だな。 for(Engineer one : engineers){ if (false == one.understandLambda()){ if (false == one.hopeUnderstandingLambda()){ System.out.println(one.name + "老害乙"); } } } ロジックほぼ一緒だし、共通化したいな、 でもどうやってやろうかな? わりかし面倒だな、もやもやするな ※罵倒されてたことは忘れて、  共通化したい願望が高まってるはず  ふふふ 寛大な気持ちで、もう一度、罵倒ロジックをプログラム化してみよう
  6. だいたいこんな感じ “Lambda知らない人”抽出機能 “Lambda知りたくない人”抽出機能 罵倒機能 「機能」もInput 機能の結合 (2段階filter&ループ処理) 第1 Filter 第2

    Filter ForEach 関連付け “Lambda知らない人”抽出機能 “Lambda知りたくない人”抽出機能 罵倒機能 関数型プログラミング if/forの制御でなく、機能 の結合で処理を記述するス タイル 数学で言う、合成関数、と か、写像の合成とかに近い? h(g(f(x))) みたいな
  7. だいたいこんな感じ “Lambda知らない人”抽出機能 “Lambda知りたくない人”抽出機能 罵倒機能 「機能」もInput 機能の結合 (2段階filter&ループ処理) 第1 Filter 第2

    Filter ForEach 関連付け “Lambda知らない人”抽出機能 “Lambda知りたくない人”抽出機能 罵倒機能 Lambda記法 「機能」の定義法。関数型プログラミ ングで使いやすいフォーマット。 「機能」をパラメータとして渡せる 関数型プログラミング if/forの制御でなく、機能 の結合で処理を記述するス タイル 数学で言う、合成関数、と か、写像の合成とかに近い? h(g(f(x))) みたいな
  8. だいたいこんな感じ “Lambda知らない人”抽出機能 “Lambda知りたくない人”抽出機能 罵倒機能 「機能」もInput 機能の結合 (2段階filter&ループ処理) 第1 Filter 第2

    Filter ForEach 関連付け “Lambda知らない人”抽出機能 “Lambda知りたくない人”抽出機能 罵倒機能 Lambda記法 「機能」の定義法。関数型プログラミ ングで使いやすいフォーマット。 「機能」をパラメータとして渡せる Stream 機能の結合、関連付けを簡単に行える フレームワークを提供 パイプ処理みたいに、処理を繋げてい くイメージ。 関数型プログラミング if/forの制御でなく、機能 の結合で処理を記述するス タイル 数学で言う、合成関数、と か、写像の合成とかに近い? h(g(f(x))) みたいな
  9. Lambda記法とStreamの実例 // Listの要素をStream使って処理する例。 // lambda記法で記述した処理を、関数に渡す。 // List生成 <Engineer> engineers =

    new ArrayList(); --- (Engineerクラスの定義とか、List構築のあたりは略) ---- // Stream生成し、Listに対しての諸々の処理を、パイプみたいに繋げていく engineers.stream() // stream生成 .filter( one -> one.understandLambda() == false ) .filter( one -> one.hopeUnderstandingLambda() == false ) .forEach( one -> System.out.println(one.name + “老害乙” ); ラムダ記法で書いた「関数」で、実際の処理 内容を記述。 “引数 -> 処理内容”というフォーマット。 これを、filter, forEachメソッドに渡す。
  10. StreamのMethod filter, forEachだけでなく、いろんな処理があります。適宜組み合わせましょう https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html allMatch() anyMatch() builder() collect() concat() count()

    distinct() empty() filter() findAny() findFirst() flatMap() flatMapToDouble() flatMapToInt() flatMapToLong() forEach() forEachOrdered() generate() iterate() limit() map() mapToDouble() mapToInt() mapToLong() max() min() noneMatch() of() peek() reduce() skip() sorted() toArray()
  11. (余談) Streamの利点:並列化も楽勝  for(Engineer one : engineers){  if (false == one.understandLambda()){

      if (false == one.hopeUnderstandingLambda()){   System.out.println(one.name + "老害乙");   }  }  }  お題:これ、並列化してください えっと、スレッド使って、、、
  12. (余談) Streamの利点:並列化も楽勝  for(Engineer one : engineers){  if (false == one.understandLambda()){

      if (false == one.hopeUnderstandingLambda()){   System.out.println(one.name + "老害乙");   }  }  }  お題:これ、並列化してください えっと、スレッド使って、、、 Streamなら楽勝 // streamならこれでおk engineers.stream() .parallel() .filter( one -> one.understandLambda() == false ) .filter( one -> one.hopeUnderstandingLambda() == false ) .forEach( one -> System.out.println(one.name + “老害乙” ); //これでもいい Engineers.parallelStream() .filter( one -> one.understandLambda() == false ) .filter( one -> one.hopeUnderstandingLambda() == false ) .forEach( one -> System.out.println(one.name + “老害乙” );
  13. 「関数を渡す」と似てることは昔からやってましたが 匿名クラス(abstractなメソッドが1つ)を引数に渡してた Thread thread = new Thread( new Runnable() {

    @Override public void run() { // your operation } }); button.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { // your operation } }); まあでも冗長ですよね。クラスである必要ないし。 Lambdaで表現できる Thread thread = new Thread( ( ) -> { /*your operation*/ } ); button.setOnClickListener( ( ) -> { /*your operation*/ } ); ※ちなみにlambda記法の “->” の左側の ( ) は、「引数ナシ、を意味します」
  14. 関数型プログラミングの良さ?(※個人の感想です) • もともとの利点? • 副作用によるバグを生みづらい • 機能独立性 • 保守性 •

    合成することで柔軟に機能が組める • 最近見直されてる背景? • ビッグデータ処理、機械学習 • 並列処理(マルチコア/GPU) • Functional reactive programming • (乱暴な概要)データの変更からコールバックまでを、一連の関数合成でつなげる的な • 例) RxJava, RxAndroid, React.js, etc.