日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
小談F#與FunctionalReactiveProgramming

本文是老趙在其博客上對F#的又一篇總結,這次講解的是Functional Reactive Programming相關的一個練習。

目前創(chuàng)新互聯(lián)已為1000+的企業(yè)提供了網(wǎng)站建設、域名、網(wǎng)絡空間、網(wǎng)站托管、服務器租用、企業(yè)網(wǎng)站設計、孟村網(wǎng)站維護等服務,公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

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

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

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

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

 
 
 
  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方法會接受一個IEvent對象,以及一個用于轉換事件參數(shù)的高階函數(shù),并返回一個新的事件對象。用戶可以監(jiān)聽這個新事件。當原有事件觸發(fā)時,它的事件參數(shù)將被高階函數(shù)轉化為新的對象,并以此出發(fā)新的事件。F#可以使用|>符號來改變參數(shù)傳遞的順序,這樣代碼可以編寫得更為流暢。例如下面兩行F#代碼其實是等價的:

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

Event.filter的作用是對事件進行過濾,只有在原事件觸發(fā)時,其事件參數(shù)滿足某個條件,才會觸發(fā)新的事件對象。Event.listen則是簡單的調用IEvent對象的Add方法,它只是一個輔助函數(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,即在轉化的同時可進行過濾。只有在高接函數(shù)的返回值為Some(args)而不是None的時候,才把args作為下一個事件的參數(shù)進行觸發(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方法的作用是合并兩個(參數(shù)相同的)事件。當其中任意一個事件觸發(fā)時,則會觸發(fā)新的事件對象。此外:

 
 
 
  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方法的作用是把原有事件拆分為兩個,并在原有事件被觸發(fā)時,根據(jù)高階函數(shù)的返回值來決定觸發(fā)哪一個新事件。

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

 
 
 
  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依次調用trigger

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

 
 
 
  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方法會維護一個累加器(acc),在上面的代碼中其初始值為0,每次event事件觸發(fā)時,則會通過高階函數(shù),把事件參數(shù)計算到當前的累加器中得到新的值,并根據(jù)新的值觸發(fā)新事件。因此上面的代碼會輸出一下內容(不包括注釋):

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

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

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

有些朋友可能會想,為什么不直接使用C#來調用F#的類庫呢?原因是“不合適”。F#的類庫設計遵循了F#的語言特性,而且如前面所講,F(xiàn)#本身會對.NET的事件進行一定轉變。此外,為C#實現(xiàn)一個合適的API也是個很好的實踐過程。例如,這又是一個適合擴展方法特性的場景。在我看來,***的API應該是這樣使用的:

 
 
 
  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));  

老趙介紹的這個Functional Reactive Programming的練習,您也試試看?

【編輯推薦】

  1. F#中DSL原型設計:語法檢查和語義分析
  2. F#入門:基本語法,模式匹配及List
  3. C# Actor的尷尬與F#美麗外表下的遺憾
  4. 函數(shù)式編程語言F#:基于CLR的另一個頭等編程語言
  5. Visual Studio 2010爆F(xiàn)#二進制兼容性問題

分享文章:小談F#與FunctionalReactiveProgramming
轉載注明:http://www.dlmjj.cn/article/djicjps.html