成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Scala講座:函數式編程處理樹結構數據

開發 后端
本文繼續Scala講座第七篇的第四部分內容,本部分提供了函數式編程的比較高階一點的案例。

在學習完函數式編程的思考方法之后,嘗試一下更高級的例子吧。這次考慮一下處理類似于XML的樹結構數據的程序。既不使用循環也不使用變量如何來描述復雜的處理呢?

先出一個處理XML數據的題目。例如有如下的XML數據,有目錄和文件,目錄下有目錄和文件兩種元素。

  1. < xml> 
  2. < dir name="com"> 
  3. < dir name="mamezou"> 
  4. < file name="aaa.txt">< /file> 
  5. < file name="bbb.txt">< /file> 
  6. < /dir> 
  7. < file name="ccc.txt">< /file> 
  8. < /dir> 
  9. < file name="ddd.txt">< /file> 
  10. < /xml> 

題目的內容是從中取出文件的部分,并打印出文件名。程序的執行結果因該如下:

  1. file:aaa.txt  
  2. file:bbb.txt  
  3. file:ccc.txt  
  4. file:ddd.txt 

好,會變成怎樣的程序呢?另外,Scala有非常強大的XML處理功能,以上的功能實際上只要一兩行程序就可以完成了。但是這次為了說明函數式編程,特地不使用哪些功能,而使用簡單功能來從頭開始編碼。

Scala中XML語句可以作為語言文本(Literal)像數字和字符串一樣被處理。像下面這樣

  1. scala> val xml = < xml> 
  2. < dir name="com"> 
  3. < dir name="mamezou"> 
  4. < file name="aaa.txt">< /file> 
  5. < file name="bbb.txt">< /file> 
  6. < /dir> 
  7. < file name="ccc.txt">< /file> 
  8. < /dir> 
  9. < file name="ddd.txt">< /file> 
  10. < /xml> 
  11. xml: scala.xml.Elem =  
  12. < xml> 
  13. < dir name="com"> 
  14. :(以下略) 

沒有雙引號,一開始就寫XML文本,然后將其賦值給變量(這里是xml)。他的類型是scala.xml.Elem,父類型為scala.xml.Node,表示XML的標記。在這里包含在< xml>< /xml>標記對中的內容被綁定在變量xml上。該Node類型里有名為child的方法,返回該標記的所有子元素。例如,這里xml.child將返回以如下兩個標記為成員的類似于ArrayBuffer的數組對象。

  1. < dir name="com"> 
  2. :  
  3. < /dir> 

  1. < file name="ddd.txt"/> 

這里可以認為ArrayBuffer是列表一樣的東西。進一步調用子元素的child方法則可以得到再下一層的元素。調用。< dir name="com">標簽對象的child方法將返回緊鄰該標簽的子元素(目錄標記)。

僅使用這個方法該如何寫取得文件名的程序呢?如果是面向對象方式,則可以首先定義Dir類和File類,然后定義Dir和File類的抽象父類Node,然后沿著樹結構定義showFiles方法,然后遞歸調用該方法來取得文件名。也就是所謂的組合模式(圖1)。

Scala講座 圖1:組合模式 

Scala講座 圖1:組合模式

如果放棄面向對象而考慮純粹的命令式方法的話就會很頭疼了。因為只用for語句的話,對于每一個Dir都要用一個for循環,層次一多將會將會變得很復雜,這里省略了命令式方法的實現。

接下來用函數式方法來考慮一下。函數式的情況下,因為考慮的是對于各個元素應用函數,先從***元素開始考慮應用什么函數。這個函數功能是“在某一時刻返回某一元素下的文件列表”。這樣就可以想到,那元素如果是file則可直接返回包含該file的列表,如果是Dir的話則返回包含所有子文件的列表。先來看看該函數的實例。

  1. def fileFinder(node:scala.xml.Node):List[scala.xml.Node] = node.label match {  
  2. case "xml" => node.child.toList.flatMap(fileFinder)  
  3. case "dir" => node.child.toList.flatMap(fileFinder)  
  4. case "file" => List(node)  
  5. case _ => List()  

其中toList()方法為將類列表對象(ArrayBuffer)轉換為列表對象。剛才用的是類似于ArrayBuffer類的對象,這里將其轉換為標準列表后再操作,而node.label則返回XML標記的名稱。

這里開始是正題了,除了file和無匹配處理(case _ => List())部分,xml和dir處理部分是問題的關鍵,也就是node.child.toList.flatMap(fileFinder)部分。如果這里關注的是Node對象,那處理過程因該是這樣的,首先用child方法取出Node的所有子元素,然后用前面說明過的類似于map的函數對每一個子元素應用fileFinder方法并遞歸重復這一過程。那為什么這樣編碼之后就能得到Node下的所有file元素了呢?

那么flatMap原本的功能又是什么呢?讓我們將其轉換成map函數,然后看一下執行過程。將XML的結構簡單化之后將如下所示

  1. < xml> ←這里  
  2. < dir> 
  3. < dir> 
  4. < file name="aaa.txt"/> 
  5. < file name="bbb.txt"/> 
  6. < /dir> 
  7. < file name="ccc.txt"/> 
  8. < /dir> 
  9. < file name="ddd.txt"/> 
  10. < /xml> 

假如現在的要素位置是xml標記,將其子元素轉換成列表后對其各個項目應用函數。

  1. List(fileFinder(< dir>~< /dir>), fileFinder(< file …/>)) 

file的話保持原樣,如果是dir則對其子元素應用函數。

  1. List(List(fileFinder(< dir>~< /dir>),fileFinder(< file name="ccc.txt"/>)),List(< file name="ddd.txt">)) 

接著對于***個Node元素應用函數。

  1. List(List(List(< file name="aaa.txt"/>,< file name="bbb.txt"/>), List(< file name="ccc.txt"/>)), List(< file name="ddd.txt">)) 

理解上述工作過程是比較困難的,重要的是在我的腦中考慮的并不是這樣復雜的邏輯,而僅僅是實現“從一個Node元素中取出file列表”的函數的邏輯。這需要一定程度的思路切換,考慮用命令式方法來實現時實際上花了我2-3小時,而想到這個函數式方法后不到10分鐘就想通了。

感覺上好像已經完成了,但是這還不夠。剛才用map來假想的過程完成后,得到的是List里面還有List的一個復合結構,光這樣還不能被使用。那么,flatMap函數就出場了。這個函數在Scala的機制上具有同map函數同等的重要層度,將map和flatMap說成Scala函數機制的核心都不為過分。

“flatMap “函數對每一個元素應用函數參數之后將其結果以列表形式返回,這時返回結果是列表類型是關鍵。接著看一下簡單的例子吧

首先是map函數的例子。對于內容為“1,2,3,4,5 “的列表,應用x*2函數。

  1. scala> List(1,2,3,4,5)  
  2. res134: List[Int] = List(12345)  
  3. scala> res134.map(x => x * 2)  
  4. res135: List[Int] = List(246810)  

結果是List(2, 4, 6, 8, 10),即將每一個元素乘以2。題外話,還有一個叫做filter的函數,他返回過濾結果。

  1. scala> res134.filter(x => x != 3)  
  2. res136: List[Int] = List(1245)這里是返回3以外的元素。那么,接下來對于List(12345)應用如下函數。  
  3. x => x match {  
  4. case 3 => List(3.13.23.3)  
  5. case _ => List(x * 2)  

也就是,3以外的情況下使元素值翻倍,3的時候將元素分割為“3.1, 3.2, 3.3“。因此,表面上對于List(1,2,3,4,5)適用該函數后希望返回的是List(1, 2, 3.1, 3.2, 3.3, 4, 5),但用了map函數后實際上不是。

  1. scala> res134.map(x => x match {  
  2. case 3 => List(3.13.23.3)  
  3. case _ => x * 2 
  4. | })  
  5. res138: List[Any] = List(24, List(3.13.23.3), 810

結果中的確包含了3.1, 3.2, 3.3,但是以List中包含List為形式的。這樣只完成了一半,同前面的XML處理一樣現象。那么,使用一下flatMap函數吧。

  1. scala> res134.flatMap(x => x match {  
  2. case 3 => List(3.13.23.3)  
  3. case _ => List(x * 2)  
  4. | })  
  5. res139: List[AnyVal] = List(243.13.23.3810

噢!就是想要的結果。不僅包含了希望的元素,還將所有元素平攤成了一個列表。

Scala講座 圖2:組合模式flatMap函數概念圖 

Scala講座 圖2:組合模式flatMap函數概念圖

回到XML的例子中,正因為用flatMap函數代替了map函數,所以對于< xml>和< dir>部分來說,原本在遞歸調用中返回的是List,但是flatMap函數將其互相合并,攤平為單一列表了。

  1. scala> def fileFinder(node:scala.xml.Node):List[scala.xml.Node] = node.label match {  
  2. case "xml" => node.child.toList.flatMap(fileFinder)  
  3. case "dir" => node.child.toList.flatMap(fileFinder)  
  4. case "file" => List(node)  
  5. case _ => List()}  
  6. fileFinder: (scala.xml.Node)List[scala.xml.Node]  
  7. scala> fileFinder(xml).foreach(x => println("file:" + x.attribute("name").getOrElse("")))  
  8. file:aaa.txt  
  9. file:bbb.txt  
  10. file:ccc.txt  
  11. file:ddd.txt 

正如所愿的結果就一下子得到了,函數式編程真是恐怖呀!這次學的map和flatMap函數在Scala中有非常重要的意義。這可以說是函數式編程的一個高潮,理解了這個之后領悟的大門就可以說向你敞開了。這實際上還與單子(monado)這一思考方法有關,理解了map和flatMap函數之后可以說是踏出了完全掌握該思考方法的一大步。關于“單子”在本連載中還會著重說明。

【編輯推薦】

  1. 萬物皆對象:介紹Scala對象
  2. Scala的泛型:***大的特性
  3. Scala的Trait:可以包含代碼的接口
  4. Scala的模式匹配和條件類
  5. Scala類:復數類,無參方法,繼承和覆蓋
責任編輯:book05 來源: JavaEye博客
相關推薦

2009-12-11 10:44:00

Scala講座函數 scala

2009-09-24 09:41:00

Scala講座Scala

2009-09-27 15:29:00

Scala講座面向對象Scala

2010-03-11 10:34:22

Scala

2009-12-11 10:42:00

Scala講座類定義構造函數

2009-09-24 09:38:00

Scala講座第一類對象scala

2010-01-28 14:51:24

Scala后函數式

2009-07-08 16:10:24

Scala簡介面向對象函數式

2018-11-12 09:50:56

Python函數式編程數據結構

2009-12-11 10:43:00

Scala講座操作符函數

2009-06-09 13:18:56

Scala函數式命令式

2010-07-07 13:11:20

ScalaF#C#

2010-07-09 14:12:00

ScalaF#C#

2009-12-11 10:45:00

Scala講座類型系統功能

2009-09-24 09:28:00

Scala講座全局變量scala

2013-09-09 09:41:34

2009-12-11 10:43:00

Scala講座混入多重繼承類型層次

2009-07-21 17:16:34

Scala函數式指令式

2009-06-16 17:09:17

Scala面向對象函數編程

2009-07-08 16:52:29

ScalaScala教程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩免费一区 | 欧美日韩在线精品 | 国产精品久久久久久婷婷天堂 | 中文字幕亚洲专区 | 欧美a级成人淫片免费看 | 中文字幕一区二区三区四区 | 888久久久| 九九热在线视频观看这里只有精品 | 欧美激情欧美激情在线五月 | 午夜理伦三级理论三级在线观看 | 一区二区三区在线免费观看 | 欧美日一区二区 | 国产99久久 | 亚洲第一中文字幕 | 国产精品揄拍一区二区 | 伊伊综合网 | 国产精品入口麻豆www | 97视频成人 | 日韩一区二区在线视频 | 亚洲一区二区三区在线 | 欧美一区二区三区在线观看 | 日韩中文字幕高清 | 国产成人免费在线 | 四虎影视1304t | 日本在线看片 | 久久成人国产精品 | 欧美精品一区二区三区四区五区 | 欧美极品视频在线观看 | 在线免费观看欧美 | 欧美一级在线观看 | 中文字幕av第一页 | 欧美视频在线播放 | 亚洲一区 | 国产一级黄色网 | 久久成人免费视频 | 中文在线一区二区 | 亚洲精品久久久9婷婷中文字幕 | 欧美九九九 | 欧美vide | 男女免费观看在线爽爽爽视频 | 一区二区视频在线观看 |