SlideShare a Scribd company logo
Java 開發者的
函數式程式設計
Functional Programming
  for Java Developers
議程
•   前情提要
•   初探函數程式設計
•   代數資料型態
•   List 處理模式
•   不可變動特性
•   回到熟悉的 Java
前情提要




       doc.openhome.cc
議程
•   lambda
•   closure
•   動靜之間
•   沒有 lambda/closure 的 Java
•   Java SE 7 lambda/closure 提案
議程
• 一級函式與 λ 演算
• JDK8 的 Lambda 語法
• 介面預設方法(Default method)
• 擴充的 Collection 框架
• 函數式風格的可能性
函數式程式設計?
•   JDK8 的 Lambda 語法
•   一級函式概念
•   函數式設計
•    λ 演算
函數式程式設計?
• Joel Spolsky
  具備一級函式的程式語言,能讓你找到更多抽象
  化的機會 - 《約耳續談軟體》
• Simon Peyton Jones
  – 純函數式領域中學到的觀點和想法,可能會給
    主流領域帶來資訊、帶來啟發 - 《編程的頂尖
    對話》
函數式程式設計?
• I Have to Be Good at Writing
  Concurrent Programs
• Most Programs Are Just Data
  Management Problems
• Functional Programming Is
  More Modular
• I Have to Work Faster and
  Faster
• Functional Programming Is a
  Return to Simplicity
初探函數程式設計
• 費式數的數學定義
  F0 = 0
  F1 = 1
  Fn = Fn-1 + Fn-2

• 指令式程設(Imperative programming)
 int fib(int n) {
     int a = 1; int b = 1;
     for(int i = 2; i < n; i++) {
         int tmp = b; b = a + b; a = tmp;
     }
     return b;
 }
• 費式數的數學定義
 F0 = 0
 F1 = 1
 Fn = Fn-1 + Fn-2

• 函數式程設
 int fib(int n) {
     if(n == 0 || n == 1) return n;
     else return fib(n - 1) + fib(n - 2);
 }
• 費式數的數學定義
 F0 = 0
 F1 = 1
 Fn = Fn-1 + Fn-2

• 函數式程設(Haskell)
 fib 0 = 0
 fib 1 = 1
 fib n = fib (n-1) + fib (n-2)
初探函數程式設計
• 使用非函數式語言可能撰寫出函數式風格
  式,然而可能…
 – 事倍功半
 – 可讀性變差
 – 執行效能不好
• 純函數式語言
 – Haskell
• 多重典範語言
 – Scala
初探函數程式設計
• 將問題分解為子問題才是重點
 – 遞迴只是程式語法上表現子問題外在形式
• 命令式加總數列
 – 變數 sum 初始值為 0,逐一取得數列元素與
   sum 相加後更新 sum,直到沒有下個元素後傳
   回 sum …(命令電腦如何求解)
 int sum(int[] nums) {
     int sum = 0;
     for(int num : nums) { sum += num; }
     return sum;
 }
初探函數程式設計
• 函數式、宣告式(Declarative)加總數列
 – 空數列為 0(廢話?XD)
 – 非空數列為首元素加上剩餘數列加總
 sum [] = 0
 sum (x:xs) = x + sum xs



• 將問題定義出來…
初探函數程式設計
• 等等 … 現在談的是 Java …
• 用 Java 以函數式風格解這問題得先談談 …
 – 代數資料型態(Algebraic data type)
 – List 處理模式
 – 不可變動性
代數資料型態
代數資料型態
• 抽象資料型態(Abstract data type)
 – 封裝結構與操作,僅透露互動時的規格
• 代數資料型態(Algebraic data type)
 – 揭露資料的結構與規律性,使之易於分而治之
   (Divide and conquer)
代數資料型態
• 重新定義 List(使用 JDK8 新語法)
public static interface List<T> {
    T head() default { return null; }
    List<T> tail() default { return null; }
}


• 非空 List 就是由尾端 List 與 前端元素組成
        head
                             …




                         tail
代數資料型態
• 空 List(沒頭沒尾)
 List<? extends Object> Nil = new List<Object>() {
     public String toString() { return "[]"; }
 };
 public static <T> List<T> nil() {
     return (List<T>) Nil;
 }

• 具有單元素的 List 就是 …

 …            Nil
          x    xs
代數資料型態
  • 在 List(xs) 前置一個元素(x)
public static <T> List<T> cons(final T x, final List<T> xs) {
    return new List<T>() {
               private T head;
               private List<T> tail;
               { this.head = x; this.tail = xs; }
               public T head(){ return this.head; }
               public List<T> tail() { return this.tail; }
               public String toString() {
                   return head() + ":" + tail();
               }
           };
}
代數資料型態
• 具有單元素 1 的 List 就是 …
cons(1, nil())                     // 1:[]

• 具有元素 2、1 的 List 就是 …
cons(2, cons(1, nil()))            // 2:1:[]

• 具有元素 3、2、1 的 List 就是 …
cons(3, cons(2, cons(1, nil())))   // 3:2:1:[]
代數資料型態
     • 為了方便 …
    public static <T> List<T> list(T... elems) {
       if(elems.length == 0) return nil();
       T[] remain = Arrays.copyOfRange(elems, 1,   elems.length);
       return cons(elems[0], list(remain));
}

     • 具有元素 3、2、1 的 List 就是 …
      list(3, 2, 1)      // 3:2:1:[]
代數資料型態
• 代數資料型態(Algebraic data type)
 – 揭露資料的結構與規律性,使之易於分而治之
   (Divide and conquer)
List 處理模式
List 處理模式
• 函數式、宣告式加總數列
   – 空數列為 0
   – 非空數列為首元素加上尾端數列加總
   sum [] = 0
   sum (x:xs) = x + sum xs

public static Integer sum(List<Integer> lt) {
    if(lt == Nil) return 0;
    else return ((Integer) lt.head()) + sum(lt.tail());
}

   sum(list(1, 2, 3, 4, 5))        // 15
• 如果想將一組整數都加一
    – 空數列為空數列
    – 非空數列為首元素加 1 結合加一後的尾端數列
public static List<Integer> addOne(List<Integer> lt) {
  if(lt == Nil) return (List<Integer>) Nil;
  else return cons((Integer) lt.head() + 1, addOne(lt.tail()));
}


 • 如果想讓一組整數都減 2
    – +1 改為 –2,addOne 改為 subtractTwo

 • 如果想讓一組整數都乘 3
    – +1 改為 *3,addOne 改為 multiplyThree
• 如果 +1、-2、*3 是個可傳入的函式
 • 咦?JDK8 的 Lambda 不就是一級函式概念?
 • 定義函式介面(Functional interface)
    interface F1<P, R> {
        R apply(P p);
    }

 • 從一組整數對應至另一組整數是 List 常見
   處理模式
public static <T, R> List<R> map(List<T> lt, F1<T, R> f) {
    if(lt == nil()) return nil();
    else return cons(f.apply(lt.head()), map(lt.tail(), f));
}
• 如果想將一組整數都加一
 map(list(1, 2, 3, 4, 5), x -> x + 1)

• 如果想讓一組整數都減 2
 map(list(1, 2, 3, 4, 5), x -> x - 2)

• 如果想讓一組整數都乘 3
 map(list(1, 2, 3, 4, 5), x -> x * 3)

• map 很好用,有一百萬種用法 … XD
• 過濾一組整數,只留下大於 3 的部份…
public static List<Integer> greaterThanThree(List<Integer> lt) {
    if(lt == Nil) return (List<Integer>) Nil;
    else {
        if(((Integer) lt.head()) > 3)
             return cons(lt.head(), greaterThanThree(lt.tail()));
        else
             return greaterThanThree(lt.tail());
    }
}

  • 過濾一組整數,只留下小於 10 的部份…
public static List<Integer> lessThanTen(List<Integer> lt) {
    if(lt == Nil) return (List<Integer>) Nil;
    else {
        if(((Integer) lt.head()) < 10)
             return cons(lt.head(), lessThanTen(lt.tail()));
        else
             return lessThanTen(lt.tail());
    }
}
• 過濾一組整數是常見的 List 處理模式 …
public static <T> List<T> filter(List<T> lt, F1<T, Boolean> f) {
    if(lt == nil()) return nil();
    else {
        if(f.apply(lt.head()))
             return cons(lt.head(), filter(lt.tail(), f));
        else
             return filter(lt.tail(), f);
    }
}
• 過濾一組整數,只留下大於 3 的部份…
filter(list(1, 2, 3, 4, 5), x -> x > 3)       // 4:5:[]

• 過濾一組整數,只留下小於 10 的部份…
filter(list(19, 9, 7, 19, 10, 4), x -> x < 10) // 9:7:4:[]


• filter 很好用,可以設定一百萬種過濾條
  件 … XD
• 類似地,從一組整數求值,也是常見的 List
   處理模式
interface F2<P, R> {
    R apply(R r, P p);
}

public static <T, R> R reduce(List<T> lt, F2<T, R> f2, R r) {
    if(lt == nil()) return r;
    else return reduce(lt.tail(), f2, f2.apply(r, lt.head()));
}

 • 加總一組整數
reduce(list(1, 2, 3, 4, 5), (r, x) -> r + x, 0)   // 15
• reduce 別名 foldLeft
            +


        0
            1   2   3   4   5




• 從左邊開始折紙…
• reduce 別名 foldLeft
               +


           1
               2   3   4   5




• 一折…
• reduce 別名 foldLeft
                  +


              3
                  3   4   5




• 二折…
• reduce 別名 foldLeft
                        +


                6
                    4       5




• 三折…
• reduce 別名 foldLeft
                            +


                   10
                        5




• 四折…
• reduce 別名 foldLeft



                       15
                        5
                        5
                        5




• 折完收工…XD
• reduce 很好用,可以有一百萬種求值方
  式 … XD
不可變動特性
• 流程中變數可變動
 – 容易設計出貫穿函式前後的流程,而不易將問題分
   解為子問題
• 函式引用可變動非區域變數
 – 會受到副作用(Side effect)影響,也就是不可見
   的輸入或輸出影響
• 物件狀態可變動
 – 對方法而言,物件值域(Field)就是非區域變數
 – 物件將會是副作用集合體,追蹤變數的難度提昇至
   追蹤物件狀態
 – 在多執行緒共用存取的情況下,維持物件狀態的同
   步將會更為困難
• 不可變動特性(Immutability)是函數式風
  格中的基本特性
 – 每個程式片段就易於分解為更小的片段
 – 引用了非區域變數,函式也不會有副作用
 – 物件不會是副作用集合體,也就不會有多執行
   緒下共用存取的問題
• 發現了嗎?剛剛一連串的設計中,沒有改
  變任何 List 狀態或變數參考!
 – 對應轉換首元素 + 對應轉換餘數列
 – 過濾首元素 + 過濾餘數列
 – 處理首元素 + 處理餘數列
流程控制轉換
• 迴圈的問題
 – 修改變數值或物件狀態
 – 易在迴圈中對數個變數或物件進行改變,使得演算
   流程趨於複雜
 – 迴圈中可能同時處理了數個子問題
• 迴圈的本質
 – 處理重複性問題,每次的重複操作就是一個子操作
 – 子操作就是子問題,獨立出來成為函式後重複呼叫
• 咦?這不就是遞迴嗎?迴圈與遞迴都是處理子
  問題的外在形式!
• 分解出子問題才是重點!
不可變動特性
• 強制將問題分解為子問題的手段
• 強制剝離邏輯泥團( Logical clump)的手段
• 因為不可變動特性,所以流程控制語法 …
 – 無法使用迴圈,使用遞迴取代
 – 必須是運算式
   • if(cond) return some;
     else return other;
   • cond : some ? other;
 – 沒有 null?
• 傳回 null 時 …
String name = selectBy(id);
if(name == null) {
    name = "guest";
}


 • 設計 getOrElse 方法
String getOrElse(String original, String replacement) {
    return original == null ? replacement : original;
}

String name = getOrElse(selectBy(id), "Guest")
• 設計 Option 物件
public class Option<T> {
    private final T value;
    public Option(T value) { this.value = value; }
    public T getOrElse(T replacement) {
        return this.value == null ? replacement : this.value;
    }
}

Option<T> selectBy(T replace) {
    ...
    return new Option(rs.next() ? rs.getString("name") : null);
}

String name = selectBy(id).getOrElse("Guest")
函數式程式設計?
• I Have to Be Good at Writing
  Concurrent Programs
• Most Programs Are Just Data
  Management Problems
• Functional Programming Is
  More Modular
• I Have to Work Faster and
  Faster
• Functional Programming Is a
  Return to Simplicity
回到熟悉的 Java
回到熟悉的 Java
•   抽象資料型態
•   命令式風格
•   可變動的變數與物件
•   那麼…


以上純屬娛樂?
回到熟悉的 Java
• 來當一下外貌協會…
map(list(1, 2, 3, 4, 5), x -> x + 1)
filter(list(1, 2, 3, 4, 5), x -> x > 3)
reduce(list(1, 2, 3, 4, 5), (r, x) -> r + x, 0)

• 若有群聰明的傢伙已經寫好這些呢?…
int sum = names.stream()
               .filter(s -> s.length() < 3)
               .map(s -> s.length())
               .reduce(0, (sum, len) -> sum + len);
• 既然他們寫好這些了,細節你怎麼會知道?
 – 延遲(Laziness)
 int sum = blocks.stream()
                 .filter(b -> b.getColor() == BLUE)
                 .map(b -> b.getWeight())
                 .reduce(0, (sum, len) -> sum + len);
 – 捷徑(short-circuiting)
 Block blueBlock = blocks.stream()
                         .filter(b -> b.getColor() == BLUE)
                         .findFirst().orElse(new Block(BLUE));

 – 平行化
 int sum = blocks.parallel()
                 .filter(b -> b.getColor() == BLUE)
                 .map(b -> b.getWeight())
                 .sum();


 – 共用資料結構
函數式程式設計?
• Joel Spolsky
  具備一級函式的程式語言,能讓你找到更多抽象
  化的機會 - 《約耳續談軟體》
• Simon Peyton Jones
  – 純函數式領域中學到的觀點和想法,可能會給
    主流領域帶來資訊、帶來啟發 - 《編程的頂尖
    對話》
回到熟悉的 Java
• 現在許多語言都是多重典範(Paradigm)
• 即便 Java 是…
 – 抽象資料型態
 – 命令式風格
 – 可變動的變數與物件
• 還是可以適當取用函數式特性…
• 你有辦法駕馭這高級的特性嗎?
回到熟悉的 Java
• 還記得右邊這本書?
 – 第一章 Customer 中
   statement 方法,如
   果將其中變數都設成
   final 會如何?
命令式與函數式
• 函數式的特性、訓練與思考只是為了…
 – 得到乾淨的程式碼
 – 培養對重複流程的敏感度
 – 能夠將問題分解為子問題
• 命令式不也就是需要這些東西嗎?
        So … Why
 Functional Programming
         matters?
延伸閱讀
• http://caterpillar.onlyfun.net/Gossip/Programme
  r/index.html
  – 程式語言的特性本質(四) 往數學方向抽象化的
    函數程式設計
  – 物件導向語言中的一級函式
  – List處理模式
  – 抽象資料型態與代數資料型態
  – 不可變動性帶來的思維轉換
• http://www.javaworld.com.tw/roller/caterpillar/c
  ategory/%E6%8A%80%E8%A1%93
  – 命令式至函數式隨記(一) ~ (六)
感謝 Orz
林信良
http://openhome.cc
caterpillar@openhome.cc

More Related Content

What's hot

Hi Haskell
Hi HaskellHi Haskell
Hi Haskell
Jifeng Deng
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closureswang hongjiang
 
functional-scala
functional-scalafunctional-scala
functional-scala
wang hongjiang
 
Ecmascript
EcmascriptEcmascript
Ecmascript
jay li
 
Ecma script edition5-小试
Ecma script edition5-小试Ecma script edition5-小试
Ecma script edition5-小试lydiafly
 
Javascript share
Javascript shareJavascript share
Javascript share
Xu Mac
 
JavaScript 快速複習 2017Q1
JavaScript 快速複習 2017Q1JavaScript 快速複習 2017Q1
JavaScript 快速複習 2017Q1
Sheng-Han Su
 
潜力无限的编程语言Javascript
潜力无限的编程语言Javascript潜力无限的编程语言Javascript
潜力无限的编程语言Javascriptjay li
 
Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍
dennis zhuang
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析
Justin Lin
 
Clojure简介与应用
Clojure简介与应用Clojure简介与应用
Clojure简介与应用
Robert Hao
 
Ch10 範例
Ch10 範例Ch10 範例
Ch10 範例
hungchiayang1
 
Js的国(转载)
Js的国(转载)Js的国(转载)
Js的国(转载)Leo Hui
 
Introduction to C++ over CLI
Introduction to C++ over CLIIntroduction to C++ over CLI
Introduction to C++ over CLI
建興 王
 
Scala
ScalaScala
Scala
deathxlent
 
Arrays的Sort算法分析
Arrays的Sort算法分析Arrays的Sort算法分析
Arrays的Sort算法分析
Zianed Hou
 
Lua 语言介绍
Lua 语言介绍Lua 语言介绍
Lua 语言介绍
gowell
 

What's hot (20)

Hi Haskell
Hi HaskellHi Haskell
Hi Haskell
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closures
 
functional-scala
functional-scalafunctional-scala
functional-scala
 
Ecmascript
EcmascriptEcmascript
Ecmascript
 
Ecma script edition5-小试
Ecma script edition5-小试Ecma script edition5-小试
Ecma script edition5-小试
 
Javascript share
Javascript shareJavascript share
Javascript share
 
JavaScript 快速複習 2017Q1
JavaScript 快速複習 2017Q1JavaScript 快速複習 2017Q1
JavaScript 快速複習 2017Q1
 
潜力无限的编程语言Javascript
潜力无限的编程语言Javascript潜力无限的编程语言Javascript
潜力无限的编程语言Javascript
 
Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析
 
Clojure简介与应用
Clojure简介与应用Clojure简介与应用
Clojure简介与应用
 
Ch5
Ch5Ch5
Ch5
 
Ch5
Ch5Ch5
Ch5
 
Ch10 範例
Ch10 範例Ch10 範例
Ch10 範例
 
Js的国(转载)
Js的国(转载)Js的国(转载)
Js的国(转载)
 
Introduction to C++ over CLI
Introduction to C++ over CLIIntroduction to C++ over CLI
Introduction to C++ over CLI
 
Scala
ScalaScala
Scala
 
Arrays的Sort算法分析
Arrays的Sort算法分析Arrays的Sort算法分析
Arrays的Sort算法分析
 
Lua 语言介绍
Lua 语言介绍Lua 语言介绍
Lua 语言介绍
 
Ch 8
Ch 8Ch 8
Ch 8
 

Similar to Java 開發者的函數式程式設計

Scala+spark 2nd
Scala+spark 2ndScala+spark 2nd
Scala+spark 2nd
Yuanhang Wang
 
Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18
Derek Lee
 
Python 温故
Python 温故Python 温故
Python 温故
勇浩 赖
 
Python 脚本入门基础
Python 脚本入门基础Python 脚本入门基础
Python 脚本入门基础wklken
 
[Effective Kotlin 讀書會] 第八章 Efficient collection processing 導讀
[Effective Kotlin 讀書會] 第八章 Efficient collection processing 導讀[Effective Kotlin 讀書會] 第八章 Efficient collection processing 導讀
[Effective Kotlin 讀書會] 第八章 Efficient collection processing 導讀
Shengyou Fan
 
Js is js(程劭非) (1)
Js is js(程劭非) (1)Js is js(程劭非) (1)
Js is js(程劭非) (1)looneyren
 
Ch5 教學
Ch5 教學Ch5 教學
Ch5 教學
hungchiayang1
 
Return to dlresolve
Return to dlresolveReturn to dlresolve
Return to dlresolve
Angel Boy
 
ncuma_pylab.pptx
ncuma_pylab.pptxncuma_pylab.pptx
ncuma_pylab.pptx
NCU MCL
 
Python学习笔记
Python学习笔记Python学习笔记
Python学习笔记
Lingfei Kong
 
Study4.TW .NET Conf 2018 - Fp in c#
Study4.TW .NET Conf 2018  - Fp in c#Study4.TW .NET Conf 2018  - Fp in c#
Study4.TW .NET Conf 2018 - Fp in c#
Chieh Kai Yang
 
Python入門:5大概念初心者必備
Python入門:5大概念初心者必備Python入門:5大概念初心者必備
Python入門:5大概念初心者必備
Derek Lee
 
Pytables
PytablesPytables
Pytables
gowell
 
Lambda演算与邱奇编码
Lambda演算与邱奇编码Lambda演算与邱奇编码
Lambda演算与邱奇编码Qin Jian
 
Python 入門
Python 入門 Python 入門
Python 入門
Andy Yao
 
nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言
吳錫修 (ShyiShiou Wu)
 
nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言
吳錫修 (ShyiShiou Wu)
 
R intro 20140716-basic
R intro 20140716-basicR intro 20140716-basic
R intro 20140716-basic
Kevin Chun-Hsien Hsu
 
数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点thinkinlamp
 

Similar to Java 開發者的函數式程式設計 (20)

Scala+spark 2nd
Scala+spark 2ndScala+spark 2nd
Scala+spark 2nd
 
Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18
 
Python 温故
Python 温故Python 温故
Python 温故
 
Python 脚本入门基础
Python 脚本入门基础Python 脚本入门基础
Python 脚本入门基础
 
[Effective Kotlin 讀書會] 第八章 Efficient collection processing 導讀
[Effective Kotlin 讀書會] 第八章 Efficient collection processing 導讀[Effective Kotlin 讀書會] 第八章 Efficient collection processing 導讀
[Effective Kotlin 讀書會] 第八章 Efficient collection processing 導讀
 
Js is js(程劭非) (1)
Js is js(程劭非) (1)Js is js(程劭非) (1)
Js is js(程劭非) (1)
 
Ch5 教學
Ch5 教學Ch5 教學
Ch5 教學
 
Return to dlresolve
Return to dlresolveReturn to dlresolve
Return to dlresolve
 
ncuma_pylab.pptx
ncuma_pylab.pptxncuma_pylab.pptx
ncuma_pylab.pptx
 
Python学习笔记
Python学习笔记Python学习笔记
Python学习笔记
 
Ruby basic
Ruby basicRuby basic
Ruby basic
 
Study4.TW .NET Conf 2018 - Fp in c#
Study4.TW .NET Conf 2018  - Fp in c#Study4.TW .NET Conf 2018  - Fp in c#
Study4.TW .NET Conf 2018 - Fp in c#
 
Python入門:5大概念初心者必備
Python入門:5大概念初心者必備Python入門:5大概念初心者必備
Python入門:5大概念初心者必備
 
Pytables
PytablesPytables
Pytables
 
Lambda演算与邱奇编码
Lambda演算与邱奇编码Lambda演算与邱奇编码
Lambda演算与邱奇编码
 
Python 入門
Python 入門 Python 入門
Python 入門
 
nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言
 
nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言
 
R intro 20140716-basic
R intro 20140716-basicR intro 20140716-basic
R intro 20140716-basic
 
数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点
 

More from Justin Lin

Ch14 簡介 Spring Boot
Ch14 簡介 Spring BootCh14 簡介 Spring Boot
Ch14 簡介 Spring Boot
Justin Lin
 
Ch13 整合 Spring MVC/Security
Ch13 整合 Spring MVC/SecurityCh13 整合 Spring MVC/Security
Ch13 整合 Spring MVC/Security
Justin Lin
 
Ch12 Spring 起步走
Ch12 Spring 起步走Ch12 Spring 起步走
Ch12 Spring 起步走
Justin Lin
 
Ch11 簡介 JavaMail
Ch11 簡介 JavaMailCh11 簡介 JavaMail
Ch11 簡介 JavaMail
Justin Lin
 
Ch10 Web 容器安全管理
Ch10 Web 容器安全管理Ch10 Web 容器安全管理
Ch10 Web 容器安全管理
Justin Lin
 
Ch09 整合資料庫
Ch09 整合資料庫Ch09 整合資料庫
Ch09 整合資料庫
Justin Lin
 
Ch08 自訂標籤
Ch08 自訂標籤Ch08 自訂標籤
Ch08 自訂標籤
Justin Lin
 
Ch07 使用 JSTL
Ch07 使用 JSTLCh07 使用 JSTL
Ch07 使用 JSTL
Justin Lin
 
Ch06 使用 JSP
Ch06 使用 JSPCh06 使用 JSP
Ch06 使用 JSP
Justin Lin
 
Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器
Justin Lin
 
Ch04 會話管理
Ch04 會話管理Ch04 會話管理
Ch04 會話管理
Justin Lin
 
Ch03 請求與回應
Ch03 請求與回應Ch03 請求與回應
Ch03 請求與回應
Justin Lin
 
Ch02 撰寫與設定 Servlet
Ch02 撰寫與設定 ServletCh02 撰寫與設定 Servlet
Ch02 撰寫與設定 Servlet
Justin Lin
 
CH1. 簡介 Web 應用程式
CH1. 簡介 Web 應用程式CH1. 簡介 Web 應用程式
CH1. 簡介 Web 應用程式
Justin Lin
 
14. 進階主題
14. 進階主題14. 進階主題
14. 進階主題
Justin Lin
 
13.並行、平行與非同步
13.並行、平行與非同步13.並行、平行與非同步
13.並行、平行與非同步
Justin Lin
 
12. 除錯、測試與效能
12. 除錯、測試與效能12. 除錯、測試與效能
12. 除錯、測試與效能
Justin Lin
 
11. 常用內建模組
11. 常用內建模組11. 常用內建模組
11. 常用內建模組
Justin Lin
 
10. 資料永續與交換
10. 資料永續與交換10. 資料永續與交換
10. 資料永續與交換
Justin Lin
 
9. 資料結構
9. 資料結構9. 資料結構
9. 資料結構
Justin Lin
 

More from Justin Lin (20)

Ch14 簡介 Spring Boot
Ch14 簡介 Spring BootCh14 簡介 Spring Boot
Ch14 簡介 Spring Boot
 
Ch13 整合 Spring MVC/Security
Ch13 整合 Spring MVC/SecurityCh13 整合 Spring MVC/Security
Ch13 整合 Spring MVC/Security
 
Ch12 Spring 起步走
Ch12 Spring 起步走Ch12 Spring 起步走
Ch12 Spring 起步走
 
Ch11 簡介 JavaMail
Ch11 簡介 JavaMailCh11 簡介 JavaMail
Ch11 簡介 JavaMail
 
Ch10 Web 容器安全管理
Ch10 Web 容器安全管理Ch10 Web 容器安全管理
Ch10 Web 容器安全管理
 
Ch09 整合資料庫
Ch09 整合資料庫Ch09 整合資料庫
Ch09 整合資料庫
 
Ch08 自訂標籤
Ch08 自訂標籤Ch08 自訂標籤
Ch08 自訂標籤
 
Ch07 使用 JSTL
Ch07 使用 JSTLCh07 使用 JSTL
Ch07 使用 JSTL
 
Ch06 使用 JSP
Ch06 使用 JSPCh06 使用 JSP
Ch06 使用 JSP
 
Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器
 
Ch04 會話管理
Ch04 會話管理Ch04 會話管理
Ch04 會話管理
 
Ch03 請求與回應
Ch03 請求與回應Ch03 請求與回應
Ch03 請求與回應
 
Ch02 撰寫與設定 Servlet
Ch02 撰寫與設定 ServletCh02 撰寫與設定 Servlet
Ch02 撰寫與設定 Servlet
 
CH1. 簡介 Web 應用程式
CH1. 簡介 Web 應用程式CH1. 簡介 Web 應用程式
CH1. 簡介 Web 應用程式
 
14. 進階主題
14. 進階主題14. 進階主題
14. 進階主題
 
13.並行、平行與非同步
13.並行、平行與非同步13.並行、平行與非同步
13.並行、平行與非同步
 
12. 除錯、測試與效能
12. 除錯、測試與效能12. 除錯、測試與效能
12. 除錯、測試與效能
 
11. 常用內建模組
11. 常用內建模組11. 常用內建模組
11. 常用內建模組
 
10. 資料永續與交換
10. 資料永續與交換10. 資料永續與交換
10. 資料永續與交換
 
9. 資料結構
9. 資料結構9. 資料結構
9. 資料結構
 

Java 開發者的函數式程式設計

  • 1.
  • 3. 議程 • 前情提要 • 初探函數程式設計 • 代數資料型態 • List 處理模式 • 不可變動特性 • 回到熟悉的 Java
  • 4. 前情提要 doc.openhome.cc
  • 5. 議程 • lambda • closure • 動靜之間 • 沒有 lambda/closure 的 Java • Java SE 7 lambda/closure 提案
  • 6. 議程 • 一級函式與 λ 演算 • JDK8 的 Lambda 語法 • 介面預設方法(Default method) • 擴充的 Collection 框架 • 函數式風格的可能性
  • 7. 函數式程式設計? • JDK8 的 Lambda 語法 • 一級函式概念 • 函數式設計 • λ 演算
  • 8. 函數式程式設計? • Joel Spolsky 具備一級函式的程式語言,能讓你找到更多抽象 化的機會 - 《約耳續談軟體》 • Simon Peyton Jones – 純函數式領域中學到的觀點和想法,可能會給 主流領域帶來資訊、帶來啟發 - 《編程的頂尖 對話》
  • 9. 函數式程式設計? • I Have to Be Good at Writing Concurrent Programs • Most Programs Are Just Data Management Problems • Functional Programming Is More Modular • I Have to Work Faster and Faster • Functional Programming Is a Return to Simplicity
  • 11. • 費式數的數學定義 F0 = 0 F1 = 1 Fn = Fn-1 + Fn-2 • 指令式程設(Imperative programming) int fib(int n) { int a = 1; int b = 1; for(int i = 2; i < n; i++) { int tmp = b; b = a + b; a = tmp; } return b; }
  • 12. • 費式數的數學定義 F0 = 0 F1 = 1 Fn = Fn-1 + Fn-2 • 函數式程設 int fib(int n) { if(n == 0 || n == 1) return n; else return fib(n - 1) + fib(n - 2); }
  • 13. • 費式數的數學定義 F0 = 0 F1 = 1 Fn = Fn-1 + Fn-2 • 函數式程設(Haskell) fib 0 = 0 fib 1 = 1 fib n = fib (n-1) + fib (n-2)
  • 14. 初探函數程式設計 • 使用非函數式語言可能撰寫出函數式風格 式,然而可能… – 事倍功半 – 可讀性變差 – 執行效能不好 • 純函數式語言 – Haskell • 多重典範語言 – Scala
  • 15. 初探函數程式設計 • 將問題分解為子問題才是重點 – 遞迴只是程式語法上表現子問題外在形式 • 命令式加總數列 – 變數 sum 初始值為 0,逐一取得數列元素與 sum 相加後更新 sum,直到沒有下個元素後傳 回 sum …(命令電腦如何求解) int sum(int[] nums) { int sum = 0; for(int num : nums) { sum += num; } return sum; }
  • 16. 初探函數程式設計 • 函數式、宣告式(Declarative)加總數列 – 空數列為 0(廢話?XD) – 非空數列為首元素加上剩餘數列加總 sum [] = 0 sum (x:xs) = x + sum xs • 將問題定義出來…
  • 17. 初探函數程式設計 • 等等 … 現在談的是 Java … • 用 Java 以函數式風格解這問題得先談談 … – 代數資料型態(Algebraic data type) – List 處理模式 – 不可變動性
  • 19. 代數資料型態 • 抽象資料型態(Abstract data type) – 封裝結構與操作,僅透露互動時的規格 • 代數資料型態(Algebraic data type) – 揭露資料的結構與規律性,使之易於分而治之 (Divide and conquer)
  • 20. 代數資料型態 • 重新定義 List(使用 JDK8 新語法) public static interface List<T> { T head() default { return null; } List<T> tail() default { return null; } } • 非空 List 就是由尾端 List 與 前端元素組成 head … tail
  • 21. 代數資料型態 • 空 List(沒頭沒尾) List<? extends Object> Nil = new List<Object>() { public String toString() { return "[]"; } }; public static <T> List<T> nil() { return (List<T>) Nil; } • 具有單元素的 List 就是 … … Nil x xs
  • 22. 代數資料型態 • 在 List(xs) 前置一個元素(x) public static <T> List<T> cons(final T x, final List<T> xs) { return new List<T>() { private T head; private List<T> tail; { this.head = x; this.tail = xs; } public T head(){ return this.head; } public List<T> tail() { return this.tail; } public String toString() { return head() + ":" + tail(); } }; }
  • 23. 代數資料型態 • 具有單元素 1 的 List 就是 … cons(1, nil()) // 1:[] • 具有元素 2、1 的 List 就是 … cons(2, cons(1, nil())) // 2:1:[] • 具有元素 3、2、1 的 List 就是 … cons(3, cons(2, cons(1, nil()))) // 3:2:1:[]
  • 24. 代數資料型態 • 為了方便 … public static <T> List<T> list(T... elems) { if(elems.length == 0) return nil(); T[] remain = Arrays.copyOfRange(elems, 1, elems.length); return cons(elems[0], list(remain)); } • 具有元素 3、2、1 的 List 就是 … list(3, 2, 1) // 3:2:1:[]
  • 25. 代數資料型態 • 代數資料型態(Algebraic data type) – 揭露資料的結構與規律性,使之易於分而治之 (Divide and conquer)
  • 27. List 處理模式 • 函數式、宣告式加總數列 – 空數列為 0 – 非空數列為首元素加上尾端數列加總 sum [] = 0 sum (x:xs) = x + sum xs public static Integer sum(List<Integer> lt) { if(lt == Nil) return 0; else return ((Integer) lt.head()) + sum(lt.tail()); } sum(list(1, 2, 3, 4, 5)) // 15
  • 28. • 如果想將一組整數都加一 – 空數列為空數列 – 非空數列為首元素加 1 結合加一後的尾端數列 public static List<Integer> addOne(List<Integer> lt) { if(lt == Nil) return (List<Integer>) Nil; else return cons((Integer) lt.head() + 1, addOne(lt.tail())); } • 如果想讓一組整數都減 2 – +1 改為 –2,addOne 改為 subtractTwo • 如果想讓一組整數都乘 3 – +1 改為 *3,addOne 改為 multiplyThree
  • 29. • 如果 +1、-2、*3 是個可傳入的函式 • 咦?JDK8 的 Lambda 不就是一級函式概念? • 定義函式介面(Functional interface) interface F1<P, R> { R apply(P p); } • 從一組整數對應至另一組整數是 List 常見 處理模式 public static <T, R> List<R> map(List<T> lt, F1<T, R> f) { if(lt == nil()) return nil(); else return cons(f.apply(lt.head()), map(lt.tail(), f)); }
  • 30. • 如果想將一組整數都加一 map(list(1, 2, 3, 4, 5), x -> x + 1) • 如果想讓一組整數都減 2 map(list(1, 2, 3, 4, 5), x -> x - 2) • 如果想讓一組整數都乘 3 map(list(1, 2, 3, 4, 5), x -> x * 3) • map 很好用,有一百萬種用法 … XD
  • 31. • 過濾一組整數,只留下大於 3 的部份… public static List<Integer> greaterThanThree(List<Integer> lt) { if(lt == Nil) return (List<Integer>) Nil; else { if(((Integer) lt.head()) > 3) return cons(lt.head(), greaterThanThree(lt.tail())); else return greaterThanThree(lt.tail()); } } • 過濾一組整數,只留下小於 10 的部份… public static List<Integer> lessThanTen(List<Integer> lt) { if(lt == Nil) return (List<Integer>) Nil; else { if(((Integer) lt.head()) < 10) return cons(lt.head(), lessThanTen(lt.tail())); else return lessThanTen(lt.tail()); } }
  • 32. • 過濾一組整數是常見的 List 處理模式 … public static <T> List<T> filter(List<T> lt, F1<T, Boolean> f) { if(lt == nil()) return nil(); else { if(f.apply(lt.head())) return cons(lt.head(), filter(lt.tail(), f)); else return filter(lt.tail(), f); } }
  • 33. • 過濾一組整數,只留下大於 3 的部份… filter(list(1, 2, 3, 4, 5), x -> x > 3) // 4:5:[] • 過濾一組整數,只留下小於 10 的部份… filter(list(19, 9, 7, 19, 10, 4), x -> x < 10) // 9:7:4:[] • filter 很好用,可以設定一百萬種過濾條 件 … XD
  • 34. • 類似地,從一組整數求值,也是常見的 List 處理模式 interface F2<P, R> { R apply(R r, P p); } public static <T, R> R reduce(List<T> lt, F2<T, R> f2, R r) { if(lt == nil()) return r; else return reduce(lt.tail(), f2, f2.apply(r, lt.head())); } • 加總一組整數 reduce(list(1, 2, 3, 4, 5), (r, x) -> r + x, 0) // 15
  • 35. • reduce 別名 foldLeft + 0 1 2 3 4 5 • 從左邊開始折紙…
  • 36. • reduce 別名 foldLeft + 1 2 3 4 5 • 一折…
  • 37. • reduce 別名 foldLeft + 3 3 4 5 • 二折…
  • 38. • reduce 別名 foldLeft + 6 4 5 • 三折…
  • 39. • reduce 別名 foldLeft + 10 5 • 四折…
  • 40. • reduce 別名 foldLeft 15 5 5 5 • 折完收工…XD • reduce 很好用,可以有一百萬種求值方 式 … XD
  • 42. • 流程中變數可變動 – 容易設計出貫穿函式前後的流程,而不易將問題分 解為子問題 • 函式引用可變動非區域變數 – 會受到副作用(Side effect)影響,也就是不可見 的輸入或輸出影響 • 物件狀態可變動 – 對方法而言,物件值域(Field)就是非區域變數 – 物件將會是副作用集合體,追蹤變數的難度提昇至 追蹤物件狀態 – 在多執行緒共用存取的情況下,維持物件狀態的同 步將會更為困難
  • 43. • 不可變動特性(Immutability)是函數式風 格中的基本特性 – 每個程式片段就易於分解為更小的片段 – 引用了非區域變數,函式也不會有副作用 – 物件不會是副作用集合體,也就不會有多執行 緒下共用存取的問題 • 發現了嗎?剛剛一連串的設計中,沒有改 變任何 List 狀態或變數參考! – 對應轉換首元素 + 對應轉換餘數列 – 過濾首元素 + 過濾餘數列 – 處理首元素 + 處理餘數列
  • 44. 流程控制轉換 • 迴圈的問題 – 修改變數值或物件狀態 – 易在迴圈中對數個變數或物件進行改變,使得演算 流程趨於複雜 – 迴圈中可能同時處理了數個子問題 • 迴圈的本質 – 處理重複性問題,每次的重複操作就是一個子操作 – 子操作就是子問題,獨立出來成為函式後重複呼叫 • 咦?這不就是遞迴嗎?迴圈與遞迴都是處理子 問題的外在形式! • 分解出子問題才是重點!
  • 45. 不可變動特性 • 強制將問題分解為子問題的手段 • 強制剝離邏輯泥團( Logical clump)的手段 • 因為不可變動特性,所以流程控制語法 … – 無法使用迴圈,使用遞迴取代 – 必須是運算式 • if(cond) return some; else return other; • cond : some ? other; – 沒有 null?
  • 46. • 傳回 null 時 … String name = selectBy(id); if(name == null) { name = "guest"; } • 設計 getOrElse 方法 String getOrElse(String original, String replacement) { return original == null ? replacement : original; } String name = getOrElse(selectBy(id), "Guest")
  • 47. • 設計 Option 物件 public class Option<T> { private final T value; public Option(T value) { this.value = value; } public T getOrElse(T replacement) { return this.value == null ? replacement : this.value; } } Option<T> selectBy(T replace) { ... return new Option(rs.next() ? rs.getString("name") : null); } String name = selectBy(id).getOrElse("Guest")
  • 48. 函數式程式設計? • I Have to Be Good at Writing Concurrent Programs • Most Programs Are Just Data Management Problems • Functional Programming Is More Modular • I Have to Work Faster and Faster • Functional Programming Is a Return to Simplicity
  • 50. 回到熟悉的 Java • 抽象資料型態 • 命令式風格 • 可變動的變數與物件 • 那麼… 以上純屬娛樂?
  • 51. 回到熟悉的 Java • 來當一下外貌協會… map(list(1, 2, 3, 4, 5), x -> x + 1) filter(list(1, 2, 3, 4, 5), x -> x > 3) reduce(list(1, 2, 3, 4, 5), (r, x) -> r + x, 0) • 若有群聰明的傢伙已經寫好這些呢?… int sum = names.stream() .filter(s -> s.length() < 3) .map(s -> s.length()) .reduce(0, (sum, len) -> sum + len);
  • 52. • 既然他們寫好這些了,細節你怎麼會知道? – 延遲(Laziness) int sum = blocks.stream() .filter(b -> b.getColor() == BLUE) .map(b -> b.getWeight()) .reduce(0, (sum, len) -> sum + len); – 捷徑(short-circuiting) Block blueBlock = blocks.stream() .filter(b -> b.getColor() == BLUE) .findFirst().orElse(new Block(BLUE)); – 平行化 int sum = blocks.parallel() .filter(b -> b.getColor() == BLUE) .map(b -> b.getWeight()) .sum(); – 共用資料結構
  • 53. 函數式程式設計? • Joel Spolsky 具備一級函式的程式語言,能讓你找到更多抽象 化的機會 - 《約耳續談軟體》 • Simon Peyton Jones – 純函數式領域中學到的觀點和想法,可能會給 主流領域帶來資訊、帶來啟發 - 《編程的頂尖 對話》
  • 54. 回到熟悉的 Java • 現在許多語言都是多重典範(Paradigm) • 即便 Java 是… – 抽象資料型態 – 命令式風格 – 可變動的變數與物件 • 還是可以適當取用函數式特性… • 你有辦法駕馭這高級的特性嗎?
  • 55. 回到熟悉的 Java • 還記得右邊這本書? – 第一章 Customer 中 statement 方法,如 果將其中變數都設成 final 會如何?
  • 56. 命令式與函數式 • 函數式的特性、訓練與思考只是為了… – 得到乾淨的程式碼 – 培養對重複流程的敏感度 – 能夠將問題分解為子問題 • 命令式不也就是需要這些東西嗎? So … Why Functional Programming matters?
  • 57. 延伸閱讀 • http://caterpillar.onlyfun.net/Gossip/Programme r/index.html – 程式語言的特性本質(四) 往數學方向抽象化的 函數程式設計 – 物件導向語言中的一級函式 – List處理模式 – 抽象資料型態與代數資料型態 – 不可變動性帶來的思維轉換 • http://www.javaworld.com.tw/roller/caterpillar/c ategory/%E6%8A%80%E8%A1%93 – 命令式至函數式隨記(一) ~ (六)