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

Scala的模式匹配和條件類(lèi)

開(kāi)發(fā) 后端
本文介紹了Scala的模式匹配。模式匹配的基本思想就是試圖對(duì)一個(gè)值進(jìn)行多種模式的匹配,并且在匹配的同時(shí)將匹配值拆分成若干子項(xiàng),最后對(duì)匹配值與其子項(xiàng)執(zhí)行某些代碼。

本文源自Michel Schinz和Philipp Haller所寫(xiě)的A Scala Tutorial for Java programmers,由Bearice成中文。第一篇為Scala簡(jiǎn)單做了一下入門(mén),第二篇描述Scala對(duì)象,第三篇對(duì)Scala類(lèi)做了一些介紹。這一篇將介紹Scala中的一個(gè)重要特性:Scala的模式匹配。

51CTO編輯推薦:Scala編程語(yǔ)言專(zhuān)題

6 Scala的模式匹配和條件類(lèi)

樹(shù)是在程序中常用的一個(gè)數(shù)據(jù)結(jié)構(gòu)。例如編譯器和解析器常常吧程序表示為樹(shù);XML文檔結(jié)構(gòu)也是樹(shù)狀的;還有一些集合是基于樹(shù)的,例如紅黑樹(shù)。

接下來(lái)我們將通過(guò)一個(gè)計(jì)算器程序來(lái)研究樹(shù)在Scala中是如何表示和操縱的。這個(gè)程序的目標(biāo)是處理一些由整數(shù)常量、變量和加號(hào)組成的簡(jiǎn)單的算數(shù)表達(dá)式,例如1 + 2 和 (x + x ) + (7 + y )。

我們首先要決定如何表示這些表達(dá)式。最自然的方法就是樹(shù)了,樹(shù)的節(jié)點(diǎn)表示操作符(在這里只有加法),而樹(shù)的葉節(jié)點(diǎn)表示值(這里表示常數(shù)和變量)。 在Java中,這樣的樹(shù)可以表示為一個(gè)超類(lèi)的樹(shù)的集合,節(jié)點(diǎn)由不同子類(lèi)的實(shí)例表示。而在函數(shù)式語(yǔ)言中,我們可以使用代數(shù)類(lèi)型(algebraic data-type)來(lái)達(dá)到同樣的目的。Scala提供了一種介于兩者之間的叫做條件類(lèi)(Case Classes)的東西。

abstract class Tree
case class Sum(l: Tree, r: Tree) extends Tree
case class Var(n: String) extends Tree
case class Const(v: Int) extends Tree

我們實(shí)際上定義了三個(gè)條件類(lèi) Sum ,Var 和 Const 。這些類(lèi)和普通類(lèi)有若干不同:

  1. 實(shí)例化時(shí)可以省略new關(guān)鍵字(例如你可以使用 Const(5)而不必使用 new Const(5) )
  2. 參數(shù)的getter函數(shù)自動(dòng)定義(例如你可以通過(guò)c.v來(lái)訪問(wèn)類(lèi)Const的實(shí)例c在實(shí)例化時(shí)獲取的參數(shù)v)
  3. 擁有默認(rèn)的預(yù)定義equals和hashCode實(shí)現(xiàn),這些實(shí)現(xiàn)可以按照值區(qū)別類(lèi)實(shí)例是否相等,而不是通過(guò)用。
  4. 擁有默認(rèn)的toString實(shí)現(xiàn)。這些實(shí)現(xiàn)返回值的代碼實(shí)現(xiàn)(例如表達(dá)式x+1可以被表達(dá)成Sum(Var(x),Const(1)))
  5. 條件類(lèi)的實(shí)例可以通過(guò)模式匹配進(jìn)行分析,我們接下來(lái)就要講這個(gè)特性。

現(xiàn)在我們已經(jīng)定義了表示我們算數(shù)表達(dá)式的數(shù)據(jù)類(lèi)型,于是我們可以開(kāi)始給他們定義對(duì)應(yīng)的操作。我們將會(huì)首先編寫(xiě)一個(gè)在上下文中下計(jì)算表達(dá)式的函數(shù)。這里的上下文指的是變量與值的綁定關(guān)系。例如表達(dá)式x+1在x=5上下文中應(yīng)該得出結(jié)果6。

這樣一來(lái)我們需要找到一個(gè)表示這種綁定關(guān)系的方法。當(dāng)然我們可以使用某種類(lèi)似hash-table的數(shù)據(jù)結(jié)構(gòu),不過(guò)我們也可以直接使用函數(shù)!一個(gè)上下文無(wú)非就是一個(gè)吧名稱(chēng)映射到值的函數(shù)。例如上面給出的{x → 5}的這個(gè)映射我們就可以在Scala中表示為:

{ case "x" => 5 }

這個(gè)定義了一個(gè)函數(shù):當(dāng)參數(shù)等于字符串"x" 時(shí)返回整數(shù)5,否則拋出異常。

在編寫(xiě)求值函數(shù)之前我們,我們需要給我們的上下文起個(gè)名字,以便在后面的代碼里面引用。理所應(yīng)當(dāng)?shù)奈覀兪褂昧祟?lèi)型String=>Int,但是如果我們給這個(gè)類(lèi)型起個(gè)名字,將會(huì)讓程序更加簡(jiǎn)單易讀,而且更加容易維護(hù)。在scala中,這件事情可以通過(guò)以下代碼完成:

type Environment = String => Int

從現(xiàn)在開(kāi)始,類(lèi)型Environment就當(dāng)作String到Int的函數(shù)類(lèi)型名來(lái)使用了。

現(xiàn)在我們可以開(kāi)始定義求值函數(shù)了。從概念上來(lái)說(shuō),這是很簡(jiǎn)單的一個(gè)過(guò)程:兩個(gè)表達(dá)式之和等于兩個(gè)表達(dá)式分別求值后再求和;變量的值可以從上下文中提??;常量的值就是他本身。在Scala中表達(dá)這個(gè)沒(méi)有什么難度:

def eval(t: Tree, env: Environment): Int = t match {
       
case Sum(l, r) => eval(l, env) + eval(r, env)
       
case Var(n)     => env(n)
       
case Const(v)   => v
}

求值函數(shù)通過(guò)對(duì)樹(shù)t進(jìn)行模式匹配來(lái)完成工作。直觀的來(lái)看,上述代碼的思路是十分清晰的:

  1. 第一個(gè)模式檢查傳入的樹(shù)的根節(jié)點(diǎn)是否是一個(gè)Sum,如果是,它將會(huì)吧樹(shù)的左邊子樹(shù)賦值給l,右邊的子樹(shù)賦值給r,然后按照箭頭后面的代碼進(jìn)行處理;這里的代碼可以(并且的確)使用了在左邊匹配時(shí)所綁定的變量,比如這里的l和r。
  2. 如果第一個(gè)檢查沒(méi)有成功,表明傳入的樹(shù)不是Sum,程序繼續(xù)檢查他是不是一個(gè)Var;如果是,則吧變量名賦給n然后繼續(xù)右邊的操作。
  3. 如果第二個(gè)檢查也失敗了,表示t既不是Sum也不是Var,程序檢查他是不是Const。如果是著賦值變量并且繼續(xù)。
  4. 最后,如果所有檢查都失敗了。就拋出一個(gè)異常表示模式匹配失敗。這只有在Tree的其他之類(lèi)被定義時(shí)才可能發(fā)生。

我們可以看出模式匹配的基本思想就是試圖對(duì)一個(gè)值進(jìn)行多種模式的匹配,并且在匹配的同時(shí)將匹配值拆分成若干子項(xiàng),最后對(duì)匹配值與其子項(xiàng)執(zhí)行某些代碼。

一個(gè)熟練的面向?qū)ο蟮某绦騿T可能想知道為什么我們不吧eval定義為T(mén)ree或者其之類(lèi)的成員函數(shù)。我們事實(shí)上可以這么做。因?yàn)镾cala允許條件類(lèi)象普通類(lèi)那樣定義成員。決定是否使用模式匹配或者成員函數(shù)取決于程序員的喜好,不過(guò)這個(gè)取舍還和可擴(kuò)展性有重要聯(lián)系:

  1. 當(dāng)你使用成員函數(shù)時(shí),你可以通過(guò)繼承Tree從而很容易的添加新的節(jié)點(diǎn)類(lèi)型,但是另外一方面,添加新的操作也是很繁雜的工作,因?yàn)槟悴坏貌恍薷腡ree的所有子類(lèi)。
  2. 當(dāng)你使用模式匹配是,形勢(shì)正好逆轉(zhuǎn)過(guò)來(lái),添加新的節(jié)點(diǎn)類(lèi)型要求你修改所有的對(duì)樹(shù)使用模式匹配的函數(shù),但是另一方面,添加一個(gè)新的操作只需要再添加一個(gè)模式匹配函數(shù)就可以了。

下面我們來(lái)更詳細(xì)的了解模式匹配,讓我們?cè)俳o表達(dá)式定義一個(gè)操作:對(duì)符號(hào)求導(dǎo)數(shù)。讀者們也許想先記住下面關(guān)于此操作的若干規(guī)則:

  1. 和的導(dǎo)數(shù)等于導(dǎo)數(shù)的和,
  2. 如果符號(hào)等以求導(dǎo)的符號(hào),則導(dǎo)數(shù)為1,否則為0.
  3. 參數(shù)的導(dǎo)數(shù)永遠(yuǎn)為0。

上述規(guī)則可以直接翻譯成Scala代碼:

def derive(t: Tree, v: String): Tree = t match { 
       
case Sum(l, r) => Sum(derive(l, v), derive(r, v))
       
case Var(n) if (v == n) => Const(1)
       
case _ => Const(0)
}

這個(gè)函數(shù)使用了兩個(gè)關(guān)于模式匹配的功能,首先case語(yǔ)句可以擁有一個(gè)guard子句:一個(gè)if條件表達(dá)式。除非guard的條件成立,否則該模式不會(huì)成功匹配。其次是通配符:_ 。這個(gè)模式表示和所有值匹配而不對(duì)任何變量賦值。

事實(shí)上我們還遠(yuǎn)沒(méi)有觸及模式匹配的全部精髓。但是我們限于篇幅原因不得不再此停筆了。下面我們看看這個(gè)兩個(gè)函數(shù)是如何在一個(gè)實(shí)例上運(yùn)行的。為了達(dá)到這個(gè)目前我們寫(xiě)了一個(gè)簡(jiǎn)單的main函數(shù)來(lái)對(duì)表達(dá)式(x + x ) + (7 + y )進(jìn)行若干操作:首先計(jì)算當(dāng){x → 5, y → 7}時(shí)表達(dá)式的值,然后分別對(duì)x和y求導(dǎo)。

def main(args: Array[String]) {
        val exp
: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y")))
        val env
: Environment = { case "x" => 5 case "y" => 7 }
        println
("Expression: " + exp)
        println
("Evaluation with x=5, y=7: " + eval(exp, env))
        println
("Derivative relative to x:\n " + derive(exp, "x"))
        println
("Derivative relative to y:\n " + derive(exp, "y"))
}

執(zhí)行程序,我們能得到以下輸出:

Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) Evaluation with x=5, y=7: 24
Derivative relative to x: Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0)))
Derivative relative to y: Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1)))

通過(guò)研究程序輸出,我們能看到求導(dǎo)的輸出可以在被打印之前簡(jiǎn)化,使用模式匹配定義一個(gè)簡(jiǎn)化函數(shù)是挺有意思的(不過(guò)也需要一定的技巧)工作。讀者可以嘗試自己完成這個(gè)函數(shù)。

看到這里,希望大家對(duì)Scala的模式匹配有了一個(gè)大概的理解。下面一篇將介紹Scala Trait。

【相關(guān)閱讀】

  1. Scala類(lèi):復(fù)數(shù)類(lèi),無(wú)參方法,繼承和覆蓋
  2. 萬(wàn)物皆對(duì)象:介紹Scala對(duì)象
  3. Scala入門(mén):Scala例子,以及如何與Java交互
  4. Scala開(kāi)發(fā)環(huán)境搭建與資源推薦
  5. Google App Engine上的Scala+Lift初試
責(zé)任編輯:yangsai 來(lái)源: Google Code
相關(guān)推薦

2009-06-19 11:13:47

Scalacase類(lèi)模式匹配

2009-06-16 17:54:38

Scala類(lèi)語(yǔ)法語(yǔ)義

2022-08-29 15:26:58

MySQLSQL模式

2009-07-20 16:56:51

Scala類(lèi)的定義

2019-06-27 10:35:40

JavaScript條件式匹配條件

2009-12-11 10:42:00

Scala講座類(lèi)定義構(gòu)造函數(shù)

2009-09-09 11:28:40

Scala類(lèi)

2017-02-28 15:48:11

Scala Trait設(shè)計(jì)模式框架

2009-07-22 07:53:00

Scala擴(kuò)展類(lèi)

2009-07-08 15:35:18

Case類(lèi)Scala

2009-07-22 09:31:59

Scala類(lèi)類(lèi)層級(jí)Java類(lèi)

2015-11-23 10:07:19

Swift模式匹配

2023-10-30 10:20:45

2009-12-11 10:44:00

Scala講座函數(shù) scala

2009-07-22 07:50:00

Scala二維布局庫(kù)抽象類(lèi)

2012-08-30 14:12:49

IBMdW

2009-07-21 12:24:01

Scala先決條件添加字段

2009-09-28 11:37:03

Journal.scaKestrel

2020-06-08 08:46:59

JavaScript條件類(lèi)名

2009-07-22 08:45:35

Scala超類(lèi)構(gòu)造器override修飾符
點(diǎn)贊
收藏

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