自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

小談F#與Functional Reactive Programming

開(kāi)發(fā) 后端
Functional Reactive Programming,直譯為函數(shù)式被動(dòng)編程。本文介紹了F#中的Functional Reactive Programming。

本文是老趙在其博客上對(duì)F#的又一篇總結(jié),這次講解的是Functional Reactive Programming相關(guān)的一個(gè)練習(xí)。

最近我們搞了一些把事件當(dāng)作對(duì)象方面的工作?;谑录木幊淘俪R?jiàn)不過(guò)了,尤其是在和UI相關(guān)的WinForm,WPF,Silverlight開(kāi)發(fā)方面。把事件作為一等公民看待之后,我們可以實(shí)現(xiàn)一些較為“特別”的編程模型,例如Functional Reactive Programming。這是一種較為優(yōu)雅的基于事件的處理方式,適合一些如交互式動(dòng)畫(huà),自動(dòng)控制方面的工作。即使平時(shí)不太常見(jiàn),我想作為一個(gè)“嘗試”或“練習(xí)”也是非常合適的。

我是通過(guò)F#而了解“事件即對(duì)象”以及FRP的相關(guān)內(nèi)容的,而微軟的Matthew Podwysocki最近一直在博客上撰寫(xiě)的一系列關(guān)于F#事件的文章也給了我很多信息。F#便直接體現(xiàn)了“事件即對(duì)象”的概念,它會(huì)把.NET類(lèi)庫(kù)中的事件轉(zhuǎn)化成IEvent對(duì)象,然后便可以對(duì)其進(jìn)行編程。IEvent對(duì)象是F#中表示“事件”的標(biāo)準(zhǔn)類(lèi)型,它的最關(guān)鍵的成員是Add方法,如果使用C#來(lái)表示便是:

  1. public interface IEvent<TEventArgs>  
  2. {  
  3.     void Add(Action<TEventArgs> callback);  
  4. }  

當(dāng)然,其實(shí)F#的事件并沒(méi)有那么簡(jiǎn)單,不過(guò)我們目前只需要關(guān)注至此即可(更詳細(xì)的信息您可以關(guān)注Matthew的文章或Anders Cui同學(xué)的中文版)。Add方法是為這個(gè)事件添加一個(gè)回調(diào)函數(shù),它自然會(huì)在事件觸發(fā)時(shí)被調(diào)用。而傳入的參數(shù),您可看作是C#中事件的EventArgs對(duì)象(即第二個(gè)參數(shù))。有了IEvent對(duì)象,在F#中便可以使用各種方式來(lái)響應(yīng)一個(gè)事件。例如:

  1. #light  
  2.  
  3. open System  
  4. open System.Windows.Forms  
  5.  
  6. let form = new Form(Visible = true, TopMost = true, Text =  
  7. form.MouseDown  
  8.     |> Event.map (fun args -> (args.X, args.Y))  
  9.     |> Event.filter (fun (x, y) -> x > 100 && y > 100)  
  10.     |> Event.listen (fun (x, y) -> printfn "(%d, %d)" x y)  
  11.  

Event.map方法會(huì)接受一個(gè)IEvent對(duì)象,以及一個(gè)用于轉(zhuǎn)換事件參數(shù)的高階函數(shù),并返回一個(gè)新的事件對(duì)象。用戶(hù)可以監(jiān)聽(tīng)這個(gè)新事件。當(dāng)原有事件觸發(fā)時(shí),它的事件參數(shù)將被高階函數(shù)轉(zhuǎn)化為新的對(duì)象,并以此出發(fā)新的事件。F#可以使用|>符號(hào)來(lái)改變參數(shù)傳遞的順序,這樣代碼可以編寫(xiě)得更為流暢。例如下面兩行F#代碼其實(shí)是等價(jià)的:

  1. Console.WriteLine "Hello World" 
  2. "Hello World" |> Console.WriteLine  

Event.filter的作用是對(duì)事件進(jìn)行過(guò)濾,只有在原事件觸發(fā)時(shí),其事件參數(shù)滿(mǎn)足某個(gè)條件,才會(huì)觸發(fā)新的事件對(duì)象。Event.listen則是簡(jiǎn)單的調(diào)用IEvent對(duì)象的Add方法,它只是一個(gè)輔助函數(shù)。

F#的Event模塊還有其他一些方法,例如:

  1. let form = new Form(Visible = true, TopMost = true, Text = "Event Sample")  
  2. form.MouseDown  
  3.     |> Event.choose (fun args ->  
  4.          if args.X > 100 && args.Y > 100 then Some(args.X, args.Y)  
  5.          else None)  
  6.     |> Event.listen (fun (x, y) -> printfn "(%d, %d)" x y)  

Event.choose方法組合了Event.filter和Event.map,即在轉(zhuǎn)化的同時(shí)可進(jìn)行過(guò)濾。只有在高接函數(shù)的返回值為Some(args)而不是None的時(shí)候,才把a(bǔ)rgs作為下一個(gè)事件的參數(shù)進(jìn)行觸發(fā)。還有:

  1. let form = new Form(Visible = true, TopMost = true, Text = "Event Sample")  
  2. form.MouseDown  
  3.     |> Event.merge form.MouseMove  
  4.     |> Event.filter (fun args -> args.Button = MouseButtons.Left)  
  5.     |> Event.map (fun args -> (args.X, args.Y))  
  6.     |> Event.listen (fun (x, y) -> printfn "(%d, %d)" x y)  

Event.merge方法的作用是合并兩個(gè)(參數(shù)相同的)事件。當(dāng)其中任意一個(gè)事件觸發(fā)時(shí),則會(huì)觸發(fā)新的事件對(duì)象。此外:

  1. let form = new Form(Visible = true, TopMost = true, Text = "Event Sample")  
  2. let (overEvent, underEvent) =  
  3.     form.MouseMove  
  4.       |> Event.merge form.MouseDown  
  5.       |> Event.filter (fun args -> args.Button = MouseButtons.Left)  
  6.       |> Event.map (fun args -> (args.X, args.Y))  
  7.       |> Event.partition (fun (x, y) -> x > 100 && y > 100)        
  8. overEvent |> Event.listen (fun (x, y) -> printfn "Over (%d, %d)" x y)  
  9. underEvent |> Event.listen (fun (x, y) -> printfn "Under (%d, %d)" x y)  

Event.partition方法的作用是把原有事件拆分為兩個(gè),并在原有事件被觸發(fā)時(shí),根據(jù)高階函數(shù)的返回值來(lái)決定觸發(fā)哪一個(gè)新事件。

以上這些都是Matthew在博客中已經(jīng)介紹過(guò)的內(nèi)容。不過(guò)我認(rèn)為,Event模塊下還有兩個(gè)方法值得一提,那就是Event.pairwise和Event.scan。請(qǐng)看下面的代碼:

  1. let (trigger : (int -> unit), event) = Event.create()  
  2. event 
  3.     |> Event.pairwise  
  4.     |> Event.listen (fun (x, y) -> printfn "%d + %d = %d" x y (x + y))  
  5.  [1..10] |> Seq.iter trigger // 使用1到10依次調(diào)用trigger

Event.create方法將創(chuàng)建一個(gè)事件對(duì)象,返回這個(gè)事件以及它的觸發(fā)器。Event.pairwise會(huì)根據(jù)IEvent<T>對(duì)象返回一個(gè)IEvent<(T, T)>對(duì)象——(T, T)是一個(gè)元組,當(dāng)然在C#中沒(méi)有這個(gè)語(yǔ)言特性時(shí),我們可以使用IEvent<T[]>來(lái)代替。當(dāng)我們使用***次trigger方法來(lái)觸發(fā)event事件時(shí),新的事件對(duì)象并不會(huì)觸發(fā)。直到第二次及以后觸發(fā)event事件時(shí),才會(huì)把上一次的事件參數(shù)和目前的事件參數(shù)“合并”,并以此來(lái)觸發(fā)新的事件。因此,上面的代碼會(huì)輸出以下內(nèi)容:

  1. 1 + 2 = 3 
  2. 2 + 3 = 5 
  3. 3 + 4 = 7 
  4. 4 + 5 = 9 
  5. 5 + 6 = 11 
  6. 6 + 7 = 13 
  7. 7 + 8 = 15 
  8. 8 + 9 = 17 
  9. 9 + 10 = 19 

而Event.scan方法則可以這樣使用:

  1. let (trigger : (int -> unit), event) = Event.create()  
  2. event 
  3.     |> Event.scan (fun acc i -> acc + i) 0  
  4.     |> Event.listen (printfn "%d")  
  5.  
  6. [1..10] |> Seq.iter trigger  
  7.  

Event.scan方法會(huì)維護(hù)一個(gè)累加器(acc),在上面的代碼中其初始值為0,每次event事件觸發(fā)時(shí),則會(huì)通過(guò)高階函數(shù),把事件參數(shù)計(jì)算到當(dāng)前的累加器中得到新的值,并根據(jù)新的值觸發(fā)新事件。因此上面的代碼會(huì)輸出一下內(nèi)容(不包括注釋?zhuān)?/P>

  1. // +1  
  2. // +2  
  3. // +3  
  4. 10 // ...  
  5. 15  
  6. 21  
  7. 28  
  8. 36  
  9. 45  
  10. 55  

自然,Event.pairwise和Event.scan方法得到的新對(duì)象都是有side effect的,需要考慮線(xiàn)程安全的問(wèn)題。F#的類(lèi)庫(kù)不保證事件觸發(fā)時(shí)的線(xiàn)程安全,于是事件在使用或觸發(fā)時(shí)需要自行進(jìn)行控制。

好,那么這次的“趣味編程”就產(chǎn)生了,您能否設(shè)計(jì)并實(shí)現(xiàn)一套類(lèi)庫(kù),為C#語(yǔ)言提供這樣一個(gè)類(lèi)似的功能呢?您需要實(shí)現(xiàn)以下7種功能:
◆map
◆filter
◆choose
◆merge
◆partition
◆pairware
◆scan

有些朋友可能會(huì)想,為什么不直接使用C#來(lái)調(diào)用F#的類(lèi)庫(kù)呢?原因是“不合適”。F#的類(lèi)庫(kù)設(shè)計(jì)遵循了F#的語(yǔ)言特性,而且如前面所講,F(xiàn)#本身會(huì)對(duì).NET的事件進(jìn)行一定轉(zhuǎn)變。此外,為C#實(shí)現(xiàn)一個(gè)合適的API也是個(gè)很好的實(shí)踐過(guò)程。例如,這又是一個(gè)適合擴(kuò)展方法特性的場(chǎng)景。在我看來(lái),***的API應(yīng)該是這樣使用的:

  1. someEvent  
  2.     .Map(args => new { args.X, args.Y })  
  3.     .Filter(args => args.X + args.Y > 100)  
  4.     .Scan(0, (acc, args) => acc + args.X, args.Y)  
  5.     .Add(args => Console.WriteLine(args));  

老趙介紹的這個(gè)Functional Reactive Programming的練習(xí),您也試試看?

【編輯推薦】

  1. F#中DSL原型設(shè)計(jì):語(yǔ)法檢查和語(yǔ)義分析
  2. F#入門(mén):基本語(yǔ)法,模式匹配及List
  3. C# Actor的尷尬與F#美麗外表下的遺憾
  4. 函數(shù)式編程語(yǔ)言F#:基于CLR的另一個(gè)頭等編程語(yǔ)言
  5. Visual Studio 2010爆F(xiàn)#二進(jìn)制兼容性問(wèn)題
責(zé)任編輯:yangsai 來(lái)源: 博客園
相關(guān)推薦

2010-01-07 10:04:18

F#函數(shù)式編程

2010-01-26 08:25:06

F#語(yǔ)法F#教程

2010-04-07 16:51:59

F#

2010-01-15 08:33:13

F#F#類(lèi)型推斷F#教程

2012-03-12 12:34:02

JavaF#

2009-11-09 17:51:51

F#函數(shù)式編程

2009-08-19 09:42:34

F#并行排序算法

2009-08-13 17:39:48

F#數(shù)據(jù)類(lèi)型Discriminat

2011-06-09 09:52:41

F#

2010-04-06 15:20:56

ASP.NET MVC

2009-11-16 09:05:46

CodeTimer

2009-12-04 09:16:44

Visual Stud

2012-11-06 10:01:35

ContinuatioF#

2010-03-26 19:22:08

F#代理

2009-12-14 09:04:10

F#運(yùn)算符

2009-08-04 14:23:55

C# Actor

2009-08-13 17:25:21

F#入門(mén)

2009-12-11 13:59:35

F#

2010-05-13 09:21:44

F#Visual Stud

2010-01-04 09:40:46

F#對(duì)象
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)