Ⅰ 《scala編程中文版》pdf下載在線閱讀全文,求百度網盤雲資源
《scala編程中文版》網路網盤pdf最新全集下載:
鏈接:https://pan..com/s/16y7fDtZ1NpyhcQul-g98Eg
Ⅱ 《Scala編程思想第二版》pdf下載在線閱讀全文,求百度網盤雲資源
《Scala編程思想第二版》網路網盤pdf最新全集下載:
鏈接:https://pan..com/s/1evpCr8Rh1j49vF18Y7y0Pg
Ⅲ Scala編程語言簡介
Scala編程語言近來抓住了很多開發者的眼球 如果你粗略瀏覽Scala的網站 你會覺得Scala是一種純粹的面向對象編程語言 而又無縫地結合了命令式和函數式的編程風格 Christopher Diggins認為
不太久之前編程語言還可以毫無疑義地歸類成 命令式 或者 函數式 Scala代表了一個新的語言品種 它抹平了這些人為劃分的界限
根據David Rupp在博客中的說法 Scala可能是下下一代java 這么高的評價讓人不禁想看看它到底是什麼東西
Scala有幾項關鍵特性表明了它的面向對象的本質 例如 Scala中的每個值都是一個對象 包括基本數據類型(即布爾值 數字等)在內 連函數也是對象 另外 類可以被子類化 而且Scala還提供了基於mixin的乎遲讓組合(mixin based position)
與只支持單繼承的語言相比 Scala具有更廣泛意義上的類重用 Scala允許定義新類的時候重用 一個類中新增的成員定義(即相較於其父類的差異之處) Scala稱之為mixin類組合
Scala還包含了若干函數式語言的關鍵概念 包括高階函數(Higher Order Function) 局部套用(Currying) 嵌套函數(Nested Function) 序列解讀(Sequence Comprehensions)等等
Scala是靜態類型的 這就允許它提供泛型類 內部類 甚至多態方法(Polymorphic Method) 另外值得一提的是 Scala被特意設計成能夠與Java和 NET互操作 Scala當前版本還不能在 NET上運行(雖然上一版可以) 但按照計劃將來可以在 NET上運行
Scala可以與Java互操作 它用scalac這個編譯器把源文件編譯成Java的class文件(即在JVM上運行的位元組碼) 你可以從Scala中調用所有的Java類庫 也同樣可以從Java應用程序中調用Scala的代碼 用David Rupp的話來說
它也可以訪問現存的數之不盡的Java類庫 這讓(潛在地)遷移到Scala更加容易
這讓Scala得以使用為Java 或者 編寫的巨量的Java類庫和框架 Scala會經常性地針對這幾個版本的Java進行測試 Scala可能也可以在更早版本的Java上運行 但沒有經過正式的測試 Scala以BSD許可發布 並且數年前就已經被認為相當穩定了
說了這么多 我們還沒有回答一個問題 為什麼我要使用Scala? Scala的設計始終貫穿著一個理念
創造一種更好地支持組件的語言 (《The Scala Programming Language》 Donna Malayeri)
也就是說軟體應該由可重用的部件構造而成 Scala旨在提供一種編程語言 能夠統一和一般化分別來自面向對象和函數式兩種不同風格的關鍵概念歲局
藉著這個目標與設計 Scala得以提供一些出眾的特性 包括
* 面旦叢向對象風格
* 函數式風格
* 更高層的並發模型
Scala把Erlang風格的基於actor的並發帶進了JVM 開發者現在可以利用Scala的actor模型在JVM上設計具伸縮性的並發應用程序 它會自動獲得多核心處理器帶來的優勢 而不必依照復雜的Java線程模型來編寫程序
* 輕量級的函數語法
o 高階
o 嵌套
o 局部套用(Currying)
o 匿名
* 與XML集成
o 可在Scala程序中直接書寫XML
o 可將XML轉換成Scala類
* 與Java無縫地互操作
Scala的風格和特性已經吸引了大量的開發者 比如Debasish Ghosh就覺得
我已經把玩了Scala好一陣子 可以說我絕對享受這個語言的創新之處
lishixin/Article/program/Java/hx/201311/26873
Ⅳ 《函數式編程思維》pdf下載在線閱讀,求百度網盤雲資源
《函數式編程思維_-_Neal_Ford.epub》網路網盤免費下載:
鏈接: https://pan..com/s/1p8L4fcpx5odA8Is2nrf6Jg
Ⅳ 編程語言scala有哪些特點
Scala有互動式命令行(REPL), 可以在上面快速的試各種語法和代碼。這對學習新特性,或者實驗新想法非常有用。(第1章)
一致性: 盡管Scala融合了靜態類型系統、面向對象、函數式編程等語言特性,但卻很少能看出融合的痕跡。Scala是我見到融合最多語言特性而又不顯得雜亂的編程語言之一。
類型安全:Scala創始人是教授,他先帶領創建了Java 5編譯器,而後覺得Java有太多羈絆而發明了Scala。 Scala編譯器和類型系統非常強大,它的目標是盡量把軟體錯誤消滅在編寫過程中。 Scala類型系統是圖靈完備的,甚至可以在編譯期間解決問題。
面向對象: Scala是面向對象的編程語言,所有的變數和方法都封裝在對象中,可以把信息封裝起來供外部使用。(第2章)
函數式編程:Scala同時又是函數式編程語言,函數可以獨立存在,可以定義一個函數作為另一個函數的返回值,也可以接受函數作為函數的參數。這給組合函數帶來了很大的便利。如何把面向對象編程形容成搭積木的話,函數式編程就像拼線條,更靈活和更有創意。(第3章)
非同步編程: 由於函數式編程提倡變數不可變,使非同步編程變得非常容易。同時Scala提供的Future(第5章), 和akka類庫(第9-11章),使得非同步編程變得非常容易。
基於JVM: Scala會被編譯成為jvm bytecode,所以Scala能無縫集成已有的Java類庫。你可以非常自然的使用已經存在的非常龐大且穩定的Java類庫,比如小巧好用的apache.common.*, 或者Java上的各種工具類庫。
因為如此眾多特性,用Scala可以優雅地編寫簡潔的代碼,同時又能減少很多低級錯誤;能快速進行開發,又能保證系統性能、團隊協作和長期維護。
Ⅵ 大數據學什麼
1、Java編程技術。Java編程技術是大數據學習的基礎,Java是一種強類型語言,擁有極高的跨平台能力,可以編寫桌面應用程序、Web應用程序、分布式系統和嵌入式系統應用程序等,是大數據工程師最喜歡的編程工具,因此,想學好大數據,掌握Java基礎是必不可少的!
2、Linux命令。對於大數據開發通常是在Linux環境下進行的,相比Linux操作系統,Windows操作系統是封閉的操作系統,開源的大數據軟體很受限制,因此,想從事大數據開發相關工作,還需掌握Linux基礎操作命令。
3、Hadoop。Hadoop是大數據開發的重要框架,其核心是HDFS和MapRece,HDFS為海量的數據提供了存儲,MapRece為海量的數據提供了計算,因此,需要重點掌握,除此之外,還需要掌握Hadoop集群、Hadoop集群管理、YARN以及Hadoop高級管理等相關技術與操作!
4、Hive。Hive是基於Hadoop的一個數據倉庫工具,可以將結構化的數據文件映射為一張資料庫表,並提供簡單的sql查詢功能,可以將sql語句轉換為MapRece任務進行運行,十分適合數據倉庫的統計分析。對於Hive需掌握其安裝、應用及高級操作等。
5、Avro與Protobuf。Avro與Protobuf均是數據序列化系統,可以提供豐富的數據結構類型,十分適合做數據存儲,還可進行不同語言之間相互通信的數據交換格式,學習大數據,需掌握其具體用法。
6、ZooKeeper。ZooKeeper是Hadoop和Hbase的重要組件,是一個為分布式應用提供一致性服務的軟體,提供的功能包括:配置維護、域名服務、分布式同步、組件服務等,在大數據開發中要掌握ZooKeeper的常用命令及功能賀尺扮的實現方法。
7、HBase。HBase是一個分布式的、面向列的開源資料庫,它不同於一般的關系資料庫,更適合於非結構化數據存儲的資料庫,是一個高可靠性、高性能、面向列、可伸縮的分布式存儲系統,大數據開發需掌握HBase基礎知識、應用、架構以及高級用法等。
8、phoenix。phoenix是用Java編寫的基於JDBC API操作HBase的開源SQL引擎,其具有動態列、散列載入、查詢伺服器、追蹤、事務、用戶自定義函數、二禪灶級索引、命名空間映射、數據收集、行時間戳列、分頁查詢困鎮、跳躍查詢、視圖以及多租戶的特性,大數據開發需掌握其原理和使用方法。
9、Redis。Redis是一個key-value存儲系統,其出現很大程度補償了memcached這類key/value存儲的不足,在部分場合可以對關系資料庫起到很好的補充作用,它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便,大數據開發需掌握Redis的安裝、配置及相關使用方法。
10、Flume。Flume是一款高可用、高可靠、分布式的海量日誌採集、聚合和傳輸的系統,Flume支持在日誌系統中定製各類數據發送方,用於收集數據;同時,Flume提供對數據進行簡單處理,並寫到各種數據接受方(可定製)的能力。大數據開發需掌握其安裝、配置以及相關使用方法。
11、SSM。SSM框架是由Spring、SpringMVC、MyBatis三個開源框架整合而成,常作為數據源較簡單的web項目的框架。大數據開發需分別掌握Spring、SpringMVC、MyBatis三種框架的同時,再使用SSM進行整合操作。
12、Kafka。Kafka是一種高吞吐量的分布式發布訂閱消息系統,其在大數據開發應用上的目的是通過Hadoop的並行載入機制來統一線上和離線的消息處理,也是為了通過集群來提供實時的消息。大數據開發需掌握Kafka架構原理及各組件的作用和使用方法及相關功能的實現!
13、Scala。Scala是一門多範式的編程語言,大數據開發重要框架Spark是採用Scala語言設計的,想要學好Spark框架,擁有Scala基礎是必不可少的,因此,大數據開發需掌握Scala編程基礎知識!
14、Spark。Spark是專為大規模數據處理而設計的快速通用的計算引擎,其提供了一個全面、統一的框架用於管理各種不同性質的數據集和數據源的大數據處理的需求,大數據開發需掌握Spark基礎、SparkJob、Spark RDD、spark job部署與資源分配、Spark shuffle、Spark內存管理、Spark廣播變數、Spark SQL、Spark Streaming以及Spark ML等相關知識。
15、Azkaban。Azkaban是一個批量工作流任務調度器,可用於在一個工作流內以一個特定的順序運行一組工作和流程,可以利用Azkaban來完成大數據的任務調度,大數據開發需掌握Azkaban的相關配置及語法規則。
16、Python與數據分析。Python是面向對象的編程語言,擁有豐富的庫,使用簡單,應用廣泛,在大數據領域也有所應用,主要可用於數據採集、數據分析以及數據可視化等,因此,大數據開發需學習一定的Python知識。
Ⅶ 《Scala編程實戰》epub下載在線閱讀全文,求百度網盤雲資源
《Scala編程實戰》(Alvin Alexander)電子書網盤下載免費在線閱讀
鏈接:
書名:Scala編程實戰
豆瓣評分:8.3
作者:Alvin Alexander
出版社:機械工業出版社
原作名:Scala Cookbook
譯者:馬博文/張錦文/任曉君
出版年:2016-6-7
頁數:642
內容簡介
學習Scala語言,不僅僅意味著熟悉新的API,更重要的是一種思維方式的轉變。從原有的面向對象編程(OO)到函數式編程(FP)的思想。本書面向實際的使用場景,提供了大量的Scala實例,同時,也給出底層的原理和相關的參考。對於Scala新手來說這是一本**不錯的入門書,對於老手來說也是一本夯實基礎,檢視自己所學知識的好書。
作者簡介
Alvin Alexander走上軟體開發之路比較曲折。雖然他從得克薩斯州的A&M大學拿到了航空工程學學位,但他真正想做的卻是打棒球。成為見習工程師時,他意識到自己喜歡軟體開發和編程勝過航天工程。因此,他開始自學Fortran、C、UNIX和網路管理、sed、awk、Perl、Java、Python、Ruby、JRuby、Groovy、PHP和Scala。在這個過程中,他開了一家軟體咨詢公司,並發展到15名員工,後來公司被賣掉,幾年之後,他移居阿拉斯加州。離開阿拉斯加州之後,他出版了兩本書,(《我如何賣掉自己的業務:私人日記》和《咨詢的禪和藝術 》)。他創建了DevDaily網站,每年都有百萬級的瀏覽量,還創建了新的軟體咨詢公司Valley Programming以及一個名為Zen Foundation的非盈利性組織。
Ⅷ Scala編程的作者簡介
奧德斯基(Martin Odersky),Scala語言的創始者。他是瑞士洛桑聯邦理工學院(EPFL)教授,從2001年起他就帶領小組致力於Scala語言、標准庫和編譯器的開發。他還是Java泛型的合作設計者及當前javac參考編譯器的原作者。LexSpoon在EPFL為Scala工作了兩年,現為Google軟體工程師。Bill Venners Artima的總裁。撰寫了許多關於、Java的文章,是《深入Java虛擬機》的作者,以及Scala Test測試框架的設計者。
Ⅸ 《七周七語言理解多種編程范型》pdf下載在線閱讀全文,求百度網盤雲資源
《七周七語言理解多種編程范型》(BruceA.Tate)電子書網盤下載免費在線閱讀
鏈接: https://pan..com/s/1Nh4VtdKqhQCU-s9O4hZ17A
書名:七周七語言理解多種編程范型
豆瓣評分:8.1
作者:BruceA.Tate
出版社:人民郵電出版社
副標題:理解多種編程范型
原作名:Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages
譯者:巨成/戴瑋/白明
出版年:2012-5-8
頁數:246
內容簡介:
從計算機發展史早期的Cobol、Fortran到後來的C、Java,編程語言的家族不斷壯大。除了這些廣為人知的語言外,還涌現了Erlang、Ruby等後起之秀,它們雖被喻為小眾語言,但因其獨特性也吸引了為數不少的追隨者。
Bruce A. Tate是軟體行業的一名老兵,他有一個宏偉目標:用一本書的篇幅切中要害地探索七種不同的語言。本書就是他的成果。書中介紹了Ruby、Io、Prolog、Scala、Erlang、Clojure和Haskell這七種語言,關注每一門語言的手虧精髓和特性,重點解決如下問題:這門語言的類型模型是什麼,編程範式是什麼,如何與其交互,有哪些決策構造和核心數據結構,有哪些獨特的核心特性。
在這個飛速發展的信息時代,程序員僅僅掌握甚至精通一門語言是遠遠不夠的。了含改解多門語言蘊涵的思維方式,在編碼中互相借鑒,再挑出一兩門對自己口味的語言深入學習,這些已經成為在軟體行業中安身立命之本。從這個意義上說,每個程序員都應該看看這本書。
作者簡介:
Bruce A. Tate是RapidRed公司總裁,該公司主要為Ruby輕量級開發提供咨詢。他曾任職於IBM公畢老神司,並擔任過多家公司的客戶解決方案總監和CTO。著作有十餘本,包括榮獲Jolt大獎的《Better, Faster, Lighter Java》。
Ⅹ 面向Java開發人員的Scala指南: 構建計算器,第1 部分
摘要 特定於領域的語言已經成為一個熱門話題 很多函數性語言之所彎則以受歡迎 主要是因為納拿它們可以用於構建特定於領域的語言 鑒於此 在 面向 Java? 開發人員的 Scala 指南 系列的第 篇文章中 Ted Neward 著手構建一個簡單的計算器 DSL 以此來展示函數性語言的構建 外部 DSL 的強大功能 他研究了 Scala 的一個新的特性 case 類 並重新審視一個功能強大的特性 模式匹配
上個月的文章發表後 我又收到了一些抱怨/評論 說我迄今為止在本系列中所用的示例都沒涉及到什麼實質性的問題 當然在學習一個新語言的初期使用一些小例子是很合理的 而讀者想要看到一些更 現實的 示例 從而了解語言的深層領域和強大功能以及其優勢 這也是理所當然的 因此 在這個月的文章中 我們來分兩部分練習構建特定於領域的語言(DSL)— 本文以一個小的計算器語言為例
關於本系列
Ted Neward 將和您一起深入探討 Scala 編程語言 在這個新的 developerWorks 系列 中 您將深入了解 Sacla 並在實踐中看到 Scala 的語言功能 進行比較時 Scala 代碼和 Java 代碼將放在一起展示 但(您將發現)Scala 中的許多內容與您在 Java 編程中發現的任何內容都沒有直接關聯 而這正是 Scala 的魅力所在!如果用 Java 代碼就能夠實現的話 又何必再學習 Scala 呢?
特定於領域的語言
可能您無法(或沒有時間)承受來自於您的項目經理給您的壓力 那麼讓我直接了當地說吧 特定於領域的語言無非就是嘗試(再一次)將一個應用程序的功能放在它該屬於的地方 — 用戶的手中
通過定義一個新的用戶可以理解並直接使用的文本語言 程序員成功擺脫了不停地處理 UI 請求和功能增強的麻煩 而且這樣還可以使用戶能夠自己創建腳本以及其他的工具 用來給他們所構建的應用程序創建新的行為 雖然這個例子可能有點冒險(或許會惹來幾封抱怨的電子郵件) 但我還是要說 DSL 的最成功的例子就是 Microsoft® Office Excel 語言 用於表達電子表格單元格的各種計算和內容 甚至有些人認為 SQL 本身就是 DSL 但這次是一個旨在與關系資料庫相交互的語言(想像一下如果程序員要通過傳統 API read() / write() 調用來從 Oracle 中獲取數據的話 那將會是什麼樣子)
這里構建的 DSL 是一個簡單的計算器語言 用於獲取並計算數學表達式 其實 這里的目標是要創建一個小型語言 這個語言能夠允許用戶來輸入相對簡單的代數表達式 然後這個代碼來為它求值並產生結果 為了盡量簡單明了 該語言不會支持很多功能完善的計算器所支持的特性 但我不也不想把它的用途限定在教學上 — 該語言一定要具備足夠的可擴展性 以使讀者無需徹底改變該語言就能夠將它用作埋茄棚一個功能更強大的語言的核心 這意味著該語言一定要可以被輕易地擴展 並要盡量保持封裝性 用起來不會有任何的阻礙
關於 DSL 的更多信息
DSL 這個主題的涉及面很廣 它的豐富性和廣泛性不是本文的一個段落可以描述得了的 想要了解更多 DSL 信息的讀者可以查閱本文末尾列出的 Martin Fowler 的 正在進展中的圖書 特別要注意關於 內部 和 外部 DSL 之間的討論 Scala 以其靈活的語法和強大的功能而成為最強有力的構建內部和外部 DSL 的語言
換句話說 (最終的)目標是要允許客戶機編寫代碼 以達到如下的目的
清單 計算器 DSL 目標
// This is Java using the Calculator String s = (( * ) + ) ; double result = tedneward calcdsl Calculator evaluate(s); System out println( We got + result); // Should be
我們不會在一篇文章完成所有的論述 但是我們在本篇文章中可以學習到一部分內容 在下一篇文章完成全部內容
從實現和設計的角度看 可以從構建一個基於字元串的解析器來著手構建某種可以 挑選每個字元並動態計算 的解析器 這的確極具誘惑力 但是這只適用於較簡單的語言 而且其擴展性不是很好 如果語言的目標是實現簡單的擴展性 那麼在深入研究實現之前 讓我們先花點時間想一想如何設計語言
根據那些基本的編譯理論中最精華的部分 您可以得知一個語言處理器(包括解釋器和編譯器)的基本運算至少由兩個階段組成
● 解析器 用於獲取輸入的文本並將其轉換成 Abstract Syntax Tree(AST) ● 代碼生成器(在編譯器的情況下) 用於獲取 AST 並從中生成所需位元組碼 或是求值器(在解釋器的情況下) 用於獲取 AST 並計算它在 AST 裡面所發現的內容
擁有 AST 就能夠在某種程度上優化結果樹 如果意識到這一點的話 那麼上述區別的原因就變得更加顯而易見了 對於計算器 我們可能要仔細檢查表達式 找出可以截去表達式的整個片段的位置 諸如在乘法表達式中運算數為 的位置(它表明無論其他運算數是多少 運算結果都會是 )
您要做的第一件事是為計算器語言定義該 AST 幸運的是 Scala 有 case 類 一種提供了豐富數據 使用了非常薄的封裝的類 它們所具有的一些特性使它們很適合構建 AST
case 類
在深入到 AST 定義之前 讓我先簡要概述一下什麼是 case 類 case 類是使 scala 程序員得以使用某些假設的默認值來創建一個類的一種便捷機制 例如 當編寫如下內容時
清單 對 person 使用 case 類
case class Person(first:String last:String age:Int){}
Scala 編譯器不僅僅可以按照我們對它的期望生成預期的構造函數 — Scala 編譯器還可以生成常規意義上的 equals() toString() 和 hashCode() 實現 事實上 這種 case 類很普通(即它沒有其他的成員) 因此 case 類聲明後面的大括弧的內容是可選的
清單 世界上最短的類清單
case class Person(first:String last:String age:Int)
這一點通過我們的老朋友 javap 很容易得以驗證
清單 神聖的代碼生成器 Batman!
C:ProjectsExplorationScala>javap PersonCompiled from case scala public class Person extends java lang Object implements scala ScalaObject scala Proct java io Serializable{ public Person(java lang String java lang String int); public java lang Object proctElement(int); public int proctArity(); public java lang String proctPrefix(); public boolean equals(java lang Object); public java lang String toString(); public int hashCode(); public int $tag(); public int age(); public java lang String last(); public java lang String first();}
如您所見 伴隨 case 類發生了很多傳統類通常不會引發的事情 這是因為 case 類是要與 Scala 的模式匹配(在 集合類型 中曾簡短分析過)結合使用的
使用 case 類與使用傳統類有些不同 這是因為通常它們都不是通過傳統的 new 語法構造而成的 事實上 它們通常是通過一種名稱與類相同的工廠方法來創建的
清單 沒有使用 new 語法?
object App{ def main(args : Array[String]) : Unit = { val ted = Person( Ted Neward ) }}
case 類本身可能並不比傳統類有趣 或者有多麼的與眾不同 但是在使用它們時會有一個很重要的差別 與引用等式相比 case 類生成的代碼更喜歡按位(biise)等式 因此下面的代碼對 Java 程序員來說有些有趣的驚喜
清單 這不是以前的類
object App{ def main(args : Array[String]) : Unit = { val ted = Person( Ted Neward ) val ted = Person( Ted Neward ) val amanda = Person( Amanda Laucher ) System out println( ted == amanda: + (if (ted == amanda) Yes else No )) System out println( ted == ted: + (if (ted == ted) Yes else No )) System out println( ted == ted : + (if (ted == ted ) Yes else No )) }}/*C:ProjectsExplorationScala>scala Appted == amanda: Noted == ted: Yested == ted : Yes*/
case 類的真正價值體現在模式匹配中 本系列的讀者可以回顧一下模式匹配(參見 本系列的第二篇文章 關於 Scala 中的各種控制構造) 模式匹配類似 Java 的 switch/case 只不過它的本領和功能更加強大 模式匹配不僅能夠檢查匹配構造的值 從而執行值匹配 還可以針對局部通配符(類似局部 默認值 的東西)匹配值 case 還可以包括對測試匹配的保護 來自匹配標準的值還可以綁定於局部變數 甚至符合匹配標準的類型本身也可以進行匹配
有了 case 類 模式匹配具備了更強大的功能 如清單 所示
清單 這也不是以前的 switch
case class Person(first:String last:String age:Int);object App{ def main(args : Array[String]) : Unit = { val ted = Person( Ted Neward ) val amanda = Person( Amanda Laucher ) System out println(process(ted)) System out println(process(amanda)) } def process(p : Person) = { Processing + p + reveals that + (p match { case Person(_ _ a) if a > => they re certainly old case Person(_ Neward _) => they e from good genes case Person(first last ageInYears) if ageInYears > => first + + last + is + ageInYears + years old case _ => I have no idea what to do with this person }) }}/*C:ProjectsExplorationScala>scala AppProcessing Person(Ted Neward ) reveals that they re certainly old Processing Person(Amanda Laucher ) reveals that Amanda Laucher is years old */
清單 中發生了很多操作 下面就讓我們先慢慢了解發生了什麼 然後回到計算器 看看如何應用它們
首先 整個 match 表達式被包裹在圓括弧中 這並非模式匹配語法的要求 但之所以會這樣是因為我把模式匹配表達式的結果根據其前面的前綴串聯了起來(切記 函數性語言裡面的任何東西都是一個表達式)
其次 第一個 case 表達式裡面有兩個通配符(帶下劃線的字元就是通配符) 這意味著該匹配將會為符合匹配的 Person 中那兩個欄位獲取任何值 但是它引入了一個局部變數 a p age 中的值會綁定在這個局部變數上 這個 case 只有在同時提供的起保護作用的表達式(跟在它後邊的 if 表達式)成功時才會成功 但只有第一個 Person 會這樣 第二個就不會了 第二個 case 表達式在 Person 的 firstName 部分使用了一個通配符 但在 lastName 部分使用常量字元串 Neward 來匹配 在 age 部分使用通配符來匹配
由於第一個 Person 已經通過前面的 case 匹配了 而且第二個 Person 沒有姓 Neward 所以該匹配不會為任何一個 Person 而被觸發(但是 Person( Michael Neward ) 會由於第一個 case 中的 guard 子句失敗而轉到第二個 case)
第三個示例展示了模式匹配的一個常見用途 有時稱之為提取 在這個提取過程中 匹配對象 p 中的值為了能夠在 case 塊內使用而被提取到局部變數中(第一個 最後一個和 ageInYears) 最後的 case 表達式是普通 case 的默認值 它只有在其他 case 表達式均未成功的情況下才會被觸發
簡要了解了 case 類和模式匹配之後 接下來讓我們回到創建計算器 AST 的任務上
計算器 AST
首先 計算器的 AST 一定要有一個公用基類型 因為數學表達式通常都由子表達式組成 通過 + ( * ) 就可以很容易地看到這一點 在這個例子中 子表達式 ( * ) 將會是 + 運算的右側運算數
事實上 這個表達式提供了三種 AST 類型
● 基表達式● 承載常量值的 Number 類型● 承載運算和兩個運算數的 BinaryOperator
想一下 算數中還允許將一元運算符用作求負運算符(減號) 將值從正數轉換為負數 因此我們可以引入下列基本 AST
清單 計算器 AST(src/calc scala)
package tedneward calcdsl{ private[calcdsl] abstract class Expr private[calcdsl] case class Number(value : Double) extends Expr private[calcdsl] case class UnaryOp(operator : String arg : Expr) extends Expr private[calcdsl] case class BinaryOp(operator : String left : Expr right : Expr) extends Expr}
注意包聲明將所有這些內容放在一個包( tedneward calcdsl)中 以及每一個類前面的訪問修飾符聲明表明該包可以由該包中的其他成員或子包訪問 之所以要注意這個是因為需要擁有一系列可以測試這個代碼的 JUnit 測試 計算器的實際客戶機並不一定非要看到 AST 因此 要將單元測試編寫成 tedneward calcdsl 的一個子包
清單 計算器測試(testsrc/calctest scala)
package tedneward calcdsl test{ class CalcTest { import junit _ Assert _ @Test def ASTTest = { val n = Number( ) assertEquals( n value) } @Test def equalityTest = { val binop = BinaryOp( + Number( ) Number( )) assertEquals(Number( ) binop left) assertEquals(Number( ) binop right) assertEquals( + binop operator) } }}
到目前為止還不錯 我們已經有了 AST
再想一想 我們用了四行 Scala 代碼構建了一個類型分層結構 表示一個具有任意深度的數學表達式集合(當然這些數學表達式很簡單 但仍然很有用) 與 Scala 能夠使對象編程更簡單 更具表達力相比 這不算什麼(不用擔心 真正強大的功能還在後面)
接下來 我們需要一個求值函數 它將會獲取 AST 並求出它的數字值 有了模式匹配的強大功能 編寫這樣的函數簡直輕而易舉
清單 計算器(src/calc scala)
package tedneward calcdsl{ // object Calc { def evaluate(e : Expr) : Double = { e match { case Number(x) => x case UnaryOp( x) => (evaluate(x)) case BinaryOp( + x x ) => (evaluate(x ) + evaluate(x )) case BinaryOp( x x ) => (evaluate(x ) evaluate(x )) case BinaryOp( * x x ) => (evaluate(x ) * evaluate(x )) case BinaryOp( / x x ) => (evaluate(x ) / evaluate(x )) } } }}
注意 evaluate() 返回了一個 Double 它意味著模式匹配中的每一個 case 都必須被求值成一個 Double 值 這個並不難 數字僅僅返回它們的包含的值 但對於剩餘的 case(有兩種運算符) 我們還必須在執行必要運算(求負 加法 減法等)前計算運算數 正如常在函數性語言中所看到的 會使用到遞歸 所以我們只需要在執行整體運算前對每一個運算數調用 evaluate() 就可以了
大多數忠實於面向對象的編程人員會認為在各種運算符本身以外 執行運算的想法根本就是錯誤的 — 這個想法顯然大大違背了封裝和多態性的原則 坦白說 這個甚至不值得討論 這很顯然違背 了封裝原則 至少在傳統意義上是這樣的
在這里我們需要考慮的一個更大的問題是 我們到底從哪裡封裝代碼?要記住 AST 類在包外是不可見的 還有就是客戶機(最終)只會傳入它們想求值的表達式的一個字元串表示 只有單元測試在直接與 AST case 類合作
但這並不是說所有的封裝都沒有用了或過時了 事實上恰好相反 它試圖說服我們在對象領域所熟悉的方法之外 還有很多其他的設計方法也很奏效 不要忘了 Scala 兼具對象和函數性 有時候 Expr 需要在自身及其子類上附加其他行為(例如 實現良好輸出的 toString 方法) 在這種情況下可以很輕松地將這些方法添加到 Expr 函數性和面向對象的結合提供了另一種選擇 無論是函數性編程人員還是對象編程人員 都不會忽略到另一半的設計方法 並且會考慮如何結合兩者來達到一些有趣的效果
從設計的角度看 有些其他的選擇是有問題的 例如 使用字元串來承載運算符就有可能出現小的輸入錯誤 最終會導致結果不正確 在生產代碼中 可能會使用(也許必須使用)枚舉而非字元串 使用字元串的話就意味著我們可能潛在地 開放 了運算符 允許調用出更復雜的函數(諸如 abs sin cos tan 等)乃至用戶定義的函數 這些函數是基於枚舉的方法很難支持的
對所有設計和實現的來說 都不存在一個適當的決策方法 只能承擔後果 後果自負
但是這里可以使用一個有趣的小技巧 某些數學表達式可以簡化 因而(潛在地)優化了表達式的求值(因此展示了 AST 的有用性)
● 任何加上 的運算數都可以被簡化成非零運算數 ● 任何乘以 的運算數都可以被簡化成非零運算數 ● 任何乘以 的運算數都可以被簡化成零
不止這些 因此我們引入了一個在求值前執行的步驟 叫做 simplify() 使用它執行這些具體的簡化工作
清單 計算器(src/calc scala)
def simplify(e : Expr) : Expr = { e match { // Double negation returns the original value case UnaryOp( UnaryOp( x)) => x // Positive returns the original value case UnaryOp( + x) => x // Multiplying x by returns the original value case BinaryOp( * x Number( )) => x // Multiplying by x returns the original value case BinaryOp( * Number( ) x) => x // Multiplying x by returns zero case BinaryOp( * x Number( )) => Number( ) // Multiplying by x returns zero case BinaryOp( * Number( ) x) => Number( ) // Dividing x by returns the original value case BinaryOp( / x Number( )) => x // Adding x to returns the original value case BinaryOp( + x Number( )) => x // Adding to x returns the original value case BinaryOp( + Number( ) x) => x // Anything else cannot (yet) be simplified case _ => e } }
還是要注意如何使用模式匹配的常量匹配和變數綁定特性 從而使得編寫這些表達式可以易如反掌 對 evaluate() 惟一一個更改的地方就是包含了在求值前先簡化的調用
清單 計算器(src/calc scala)
def evaluate(e : Expr) : Double = { simplify(e) match { case Number(x) => x case UnaryOp( x) => (evaluate(x)) case BinaryOp( + x x ) => (evaluate(x ) + evaluate(x )) case BinaryOp( x x ) => (evaluate(x ) evaluate(x )) case BinaryOp( * x x ) => (evaluate(x ) * evaluate(x )) case BinaryOp( / x x ) => (evaluate(x ) / evaluate(x )) } }
還可以再進一步簡化 注意一下 它是如何實現只簡化樹的最底層的?如果我們有一個包含 BinaryOp( * Number( ) Number( )) 和 Number( ) 的 BinaryOp 的話 那麼內部的 BinaryOp 就可以被簡化成 Number( ) 但外部的 BinaryOp 也會如此 這是因為此時外部 BinaryOp 的其中一個運算數是零
我突然犯了作家的職業病了 所以我想將它留予讀者來定義 其實是想增加點趣味性罷了 如果讀者願意將他們的實現發給我的話 我將會把它放在下一篇文章的代碼分析中 將會有兩個測試單元來測試這種情況 並會立刻失敗 您的任務(如果您選擇接受它的話)是使這些測試 — 以及其他任何測試 只要該測試採取了任意程度的 BinaryOp 和 UnaryOp 嵌套 — 通過
結束語
顯然我還沒有說完 還有分析的工作要做 但是計算器 AST 已經成形 我們無需作出大的變動就可以添加其他的運算 運行 AST 也無需大量的代碼(按照 Gang of Four 的 Visitor 模式) 而且我們已經有了一些執行計算本身的工作代碼(如果客戶機願意為我們構建用於求值的代碼的話)
lishixin/Article/program/Java/hx/201311/25735