Ihome inaction 篇外篇之fp介绍

1,022 views

Published on

很早以前做的一次函数式编程的介绍,很不成熟,做个备份。

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,022
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
12
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Ihome inaction 篇外篇之fp介绍

  1. 1. Ihome In Action 函数式编程简介 Author:Dennis —— IHOME 的今生来世之篇外篇
  2. 2. 什么是 FP <ul><li>FP : Functional Programming </li></ul><ul><li>是指基于 lambda 演算的一种编程典范,函数式编程是一系列理念,而非教条。 </li></ul><ul><li>Lambda 演算: </li></ul><ul><li>一套用于研究 函数 定义、函数应用和 递归 的 形式系统 。它由 丘奇 ( Alonzo Church )和他的学生 克莱尼 ( Stephen Cole Kleene )在 20 世纪 30 年代 引入 </li></ul>
  3. 3. FP 的优点 <ul><li>Lambda 演算与图灵模型等价 </li></ul><ul><li>无副作用: </li></ul><ul><li>1 、无锁,并行, Erlang </li></ul><ul><li>2 、利于单元测试 </li></ul><ul><li>机器辅助的推理和优化 amb 操作符 </li></ul><ul><li>Less Code 更高的抽象能力带来更少的代码,更优雅的实现。 </li></ul>
  4. 4. FP 特性 <ul><li>无副作用、高阶函数、闭包、 currying 、延时求值、 Continuation </li></ul><ul><li>、模式匹配、 Mond…… </li></ul>
  5. 5. 无副作用 <ul><li>1 、所有符号都是 final ,利于单元测试和调试,只需要关心输入、输出 </li></ul><ul><li>2 、从来不会有两个线程修改同一变量的情况出现,无需锁,无需临界区,利于并行, erlang 就是个例子 </li></ul><ul><li>3 、由于函数无副作用,因此函数的组合也将是无副作用的,这在命令式语言是不可想象的 </li></ul>
  6. 6. 高阶函数 <ul><li>函数:在 scheme 中通常称为 procedure ,例子: </li></ul><ul><li>+ => #<primitive:+> </li></ul><ul><li>(define (square x) (* x x) </li></ul><ul><li>(square 3) => 9 </li></ul><ul><li>(square 4) => 10 </li></ul><ul><li>( (lambda(x) (* x x)) 3) => 9 匿名函数 </li></ul><ul><li>函数可以作为参数,作为返回值。 </li></ul><ul><li>高阶函数: high-order function ,简而言之就是操作函数的函数。注意,在 FP 中,函数是 first-class </li></ul>
  7. 7. 高阶函数例子 <ul><li>Map 、 foreach 、 filter 、 </li></ul><ul><li>map, 将 proc 作用于列表的每个元素,返回新的列表: </li></ul><ul><li>(define (map proc items) </li></ul><ul><li>(if (null? Items) </li></ul><ul><li>'() </li></ul><ul><li>(cons (proc (car items)) </li></ul><ul><li>(map proc (cdr items))))) </li></ul><ul><li>(map square (list 1 2 3 4)) </li></ul><ul><li>=> (1 4 9 16) </li></ul>
  8. 8. 高阶函数 filter <ul><li>用于过滤列表,根据谓词predicate结果判断 </li></ul><ul><li>(define (filter predicate sequence) </li></ul><ul><li>(cond ((null? sequence) '()) </li></ul><ul><li>((predicate (car sequence)) </li></ul><ul><li>(cons (car sequence) (filter predicate (cdr sequence)))) </li></ul><ul><li>(else </li></ul><ul><li>(filter predicate (cdr sequence))))) </li></ul>
  9. 9. 高阶函数 accumulate <ul><li>将列表中的元素按照操作OP累积起来 </li></ul><ul><li>(define (accumulate op initial sequence) </li></ul><ul><li>(if (null? sequence) </li></ul><ul><li>initial </li></ul><ul><li>(op (car sequence) (accumulate op initial (cdr sequence))))) </li></ul>
  10. 10. 高阶函数 合并 <ul><li>合并起来,求解下列问题: </li></ul><ul><li>求解<=n中所有fib(k)是偶数的列表 </li></ul><ul><li>第一步:生成0-n </li></ul><ul><li>(define (enumerate-interval low high) </li></ul><ul><li>(if (> low high) </li></ul><ul><li>'() </li></ul><ul><li>(cons low (enumerate-interval (+ low 1) high)))) </li></ul><ul><li>(enumerate-interval 0 n) </li></ul>
  11. 11. 高阶函数 合并 <ul><li>第二步:由map生成0-n整数对应的fib(k) </li></ul><ul><li>(map fib (enumerate-interval 0 n)) </li></ul><ul><li>第三步:过滤偶数 </li></ul><ul><li>(filter even? </li></ul><ul><li>(map fib (enumerate-interval 0 n))) </li></ul>
  12. 12. 高阶函数 合并 <ul><li>第四步:将结果累积到列表 </li></ul><ul><li>(accumulate cons </li></ul><ul><li>'() </li></ul><ul><li>(filter even? </li></ul><ul><li>(map (enumerate-interval 0 n))))) </li></ul>
  13. 13. 高阶函数 合并 <ul><li>最终结果: </li></ul><ul><li>(define (even-fibs n) </li></ul><ul><li>(accumulate cons </li></ul><ul><li>'() </li></ul><ul><li>(filter even? </li></ul><ul><li>(map fib (enumerate-interval 0 n))))) </li></ul>
  14. 14. 高阶函数 合并 <ul><li>以信息流的方式去观察,高阶函数带来了约定接口的抽象 </li></ul>
  15. 15. 闭包 - 定义 <ul><li>严格定义:闭包是包含了自由变量的代码块 </li></ul><ul><li>泛化:匿名函数, block 都归入闭包 </li></ul><ul><li>自由变量? </li></ul><ul><li>(define (get-adder x) </li></ul><ul><li>(lambda(y)(+ x y))) </li></ul><ul><li>X 就是自由变量,在离开 get-adder 作用域之后仍然可以访问。 </li></ul><ul><li>(define add4 (get-adder 4)) </li></ul><ul><li>(add4 7) => 11 </li></ul><ul><li>闭包作用:模块化、简化代码、抽象 </li></ul>
  16. 16. 闭包 - 应用举例 <ul><li>排序: </li></ul><ul><li>Java: </li></ul><ul><li>class Product implements Comparator{ </li></ul><ul><li>public int compare(Product other) </li></ul><ul><li>return this.price-other.price </li></ul><ul><li>… </li></ul><ul><li>Collections.sort(products) </li></ul><ul><li>Ruby: </li></ul><ul><li>products.sort{|a,b| a.price-b.price} </li></ul>
  17. 17. 闭包 - 应用举例 <ul><li>Java7 引入的闭包语法: </li></ul><ul><li>double log = { double x => Math.log(x) }.invoke(10); </li></ul><ul><li>int sum = { int x, int y => x + y }.invoke(3, 4); // will return 7 </li></ul><ul><li>String[] girls = { &quot;Jane&quot;, &quot;Eva&quot;, &quot;Sarah&quot;, &quot;Alice&quot; }; </li></ul><ul><li>Arrays.sort(girls, { String s1, String s2 => int r = s1.length() - s2.length(); r == 0 ? s1.compareTo(s2) : r }); </li></ul>
  18. 18. 闭包应用举例 <ul><li>模拟对象, lua: </li></ul><ul><li>function make_stack() </li></ul><ul><li>        local data = {};         local last = -1; </li></ul><ul><li>        local function push(e)             last = last + 1;             data[last] = e;         end </li></ul><ul><li>        local function pop()             if last == -1 then                 return nil             end             last = last - 1             return data[last+1]         end </li></ul><ul><li>        return function (index)             local tb = {push=push, pop=pop}             return tb[index]         end end </li></ul><ul><li>s=make_stack() </li></ul><ul><li>s(&quot;push&quot;)(&quot;test0&quot;) </li></ul><ul><li>s(&quot;push&quot;)(&quot;test1&quot;) s(&quot;push&quot;)(&quot;test2&quot;) s(&quot;push&quot;)(&quot;test3&quot;) </li></ul><ul><li>print(s(&quot;pop&quot;)()) print(s(&quot;pop&quot;)()) print(s(&quot;pop&quot;)()) </li></ul>
  19. 19. Currying <ul><li>Currying :俗称克里化,命名为了纪念逻辑学家 Haskell Curry 。它是为了解决 lambda 演算在多个参数情况下而引入的。作用是把带有多个参数的函数转化为带有单个参数的函数(其余的参数由 curry 来决定如何处理) </li></ul>
  20. 20. Currying 例子 1 <ul><li>Javascript 为例: </li></ul><ul><li>function add(x, y)  {       if(x!=null && y!=null) return x + y;   </li></ul><ul><li>     else if(x!=null && y==null)  </li></ul><ul><li>return function(y)  {            return x + y;       }  </li></ul><ul><li>     else if(x==null && y!=null) </li></ul><ul><li>  return function(x) {           return x + y;      }  }  </li></ul><ul><li>var a = add(3, 4);  </li></ul><ul><li>var b = add(2);  </li></ul><ul><li>var c = b(10);  </li></ul>
  21. 21. Currying 例子 2 <ul><li>Ruby1.9 引入了 Proc#curry </li></ul><ul><li>plus = lambda {|a,b| a + b}   </li></ul><ul><li>plus(3,4) =>7 </li></ul><ul><li>curried_plus = plus.curry </li></ul><ul><li>plus_three = curried_plus[3]  </li></ul><ul><li>plus_three(4) =>7 </li></ul>
  22. 22. Currying 例子 3 <ul><li>可以写出很优雅的代码 </li></ul><ul><li>is_weekday = lambda {|day_of_week, time| time.wday == day_of_week}.curry   </li></ul><ul><li>   </li></ul><ul><li>sunday    = is_weekday[0]   </li></ul><ul><li>monday    = is_weekday[1]   </li></ul><ul><li>tuesday   = is_weekday[2]   </li></ul><ul><li>wednesday = is_weekday[3]   </li></ul><ul><li>thursday  = is_weekday[4]   </li></ul><ul><li>friday    = is_weekday[5]   </li></ul><ul><li>saturday  = is_weekday[6]   </li></ul><ul><li>   </li></ul><ul><li>case Time.now   </li></ul><ul><li>when sunday    </li></ul><ul><li>   puts &quot;Day of rest&quot;   </li></ul><ul><li>when monday, tuesday, wednesday, thursday, friday   </li></ul><ul><li>   puts &quot;Work&quot;   </li></ul><ul><li>when saturday   </li></ul><ul><li>   puts &quot;chores&quot;   </li></ul><ul><li>end   </li></ul>
  23. 23. 延时求值 <ul><li>Lazy evalution :函数调用时,对参数的求值推迟到需要使用该参数的时候。 </li></ul><ul><li>作用: </li></ul><ul><li>1 、特殊的控制结构 </li></ul><ul><li>2 、无穷列表,流 (stream) </li></ul>
  24. 24. 特殊的控制结构 <ul><li>Unless过程: </li></ul><ul><li>(define (unless condition usual-value exception-value) </li></ul><ul><li>(if condition </li></ul><ul><li>Exception-value </li></ul><ul><li>Usual-value)) </li></ul><ul><li>(unless (= 0 0) </li></ul><ul><li>(/ 1 0) </li></ul><ul><li>(begin (display “exception return 0”) 0)) </li></ul>
  25. 25. 特殊的控制结构 <ul><li>这个函数在非延时求值器的时候将无法正确求值,因为(/ 1 0)在函数调用前将求值,抛出错误。 </li></ul><ul><li>而在一个延时求值器中,由于推迟了对参数的求值,那么在(= 0 0)返回true之后,直接求值exception-value并返回,(/ 1 0)将根本不会被调用到,从而整个函数可以正确运作。 </li></ul>
  26. 26. 无穷级数 <ul><li>相对于cons、car、cdr,我们定义相应的stream-cons stream-car stream-cdr,因此也有相应的高阶函数stream-map stream-filter等。 </li></ul><ul><li>定义自然数: (define (integers-starting-from n) </li></ul><ul><li>(cons-stream n (integers-starting-from (+ n 1)))) </li></ul><ul><li>(define integers (integers-starting-from 1)) </li></ul>
  27. 27. 无穷级数 <ul><li>定义1的无穷数列 </li></ul><ul><li>(define ones (cons-stream 1 ones)) </li></ul><ul><li>更无敌的例子,筛法求素数的无穷数列: </li></ul>
  28. 28. 筛法求素数 <ul><li>(define (divisible? x y) (= (remainder x y) 0)) </li></ul><ul><li>(define (sieve stream) </li></ul><ul><li>(cons-stream </li></ul><ul><li>(stream-car stream) </li></ul><ul><li>(sieve (stream-filter (lambda(x) </li></ul><ul><li>(not (divisible? x (stream-car stream)))) </li></ul><ul><li>(stream-cdr stream))))) </li></ul><ul><li>(define primes (sieve (integers-starting-from 2))) </li></ul>
  29. 29. Continuation <ul><li>表达式的求值分为两个阶段: </li></ul><ul><li>1 、 what to evalute? </li></ul><ul><li>2 、 waht to do with the value? </li></ul><ul><li>那么 what to do with the value 就称为表达式的延续 (continuation) </li></ul>
  30. 30. Continuation 例子 <ul><li>(if (null? x) </li></ul><ul><li>'() </li></ul><ul><li>(cdr x)) </li></ul><ul><li>表达式(null? x)求值后,根据这个表达式的值去决定执行'()还是(cdr x),这个决定的过程就是表达式(null? x)的延续. </li></ul><ul><li>(+ 2 </li></ul><ul><li>(call/cc </li></ul><ul><li>(lambda (k) </li></ul><ul><li>(* 5 (k 4))))) => 6 </li></ul>
  31. 31. Continuation 在语言中的支持 <ul><li>call/cc: call-with-current-continuation </li></ul><ul><li>该过程调用一个函数,并向此函数传入当前的continuation </li></ul><ul><li>Scheme、Ruby中都有这个call/cc支持 </li></ul><ul><li>Continuaton的作用: </li></ul><ul><li>1、实现高级的控制结构,break、goto </li></ul><ul><li>2、实现代码级别的程序调用,协程 </li></ul><ul><li>3、实现机器推理所需要的fail、try机制 </li></ul>
  32. 32. Continuation break 语句 <ul><li>scheme中是没有break的,可以通过continuation模拟: </li></ul><ul><li>(define product </li></ul><ul><li>(lambda (ls) </li></ul><ul><li>(call/cc </li></ul><ul><li>(lambda (break) </li></ul><ul><li>(let f ((ls ls)) </li></ul><ul><li>(cond </li></ul><ul><li>((null? ls) 1) </li></ul><ul><li>((= (car ls) 0) (break 0)) </li></ul><ul><li>(else (* (car ls) (f (cdr ls)))))))))) </li></ul>
  33. 33. Continuation amb 操作符 <ul><li>amb, 是 John McCarthy 在 1961 年提出的。 </li></ul><ul><li>Amb 表达式 </li></ul><ul><li>( amb e1 e2 e3 e4 ...) </li></ul><ul><li>有歧义性地返回 n 个表达式中 ei 之一的值,例如: </li></ul><ul><li>(list (amb 1 2 3) (amb 'a 'b)) </li></ul><ul><li>将有如下六个值: </li></ul><ul><li>(1 a) (1 b) (2 a) (2 b) (3 a) (3 b) </li></ul><ul><li>注意: (amb) 永远是 fail 。 </li></ul><ul><li>(define (require p) </li></ul><ul><li>(if (not p) (amb))) </li></ul>
  34. 34. amb操作符的实现 <ul><li>通过call/cc实现,amb本质上是一种失败、回溯机制,通过continuation记录堆栈的跳转来实现 </li></ul><ul><li>scheme的实现,通过宏 continuation 2.scm </li></ul><ul><li>Ruby的实现 continuation.rb </li></ul>
  35. 35. amb操作符的使用 <ul><li>一道逻辑题: </li></ul><ul><li>Baker, Cooper, Fletcher, Miller, 和Smith住在一动5层公寓的不同楼层。Baker不在顶楼住。Cooper不在底楼住。Fletcher既不在顶楼也不在底楼。Miller住的楼层比Cooper住的高。Smith和Fletcher的楼层不相邻。Fletcher和Cooper的楼层不相邻。问每个人住的楼层。 </li></ul>
  36. 36. 模式匹配

×