標簽 ‘ pattern-matching

話說模式匹配(8) 一個抽取器的例子

一個抽取器的例子

目前List的序列模式(sequence pattern)可以支持對前邊若干元素的匹配,比如:List(1,2,3,_*),如果想要實現 List(_*, lastEle) 這樣的形式,就需要通過自定義一個抽取器來實現了

// 自定義Extractor
object Append {
    // 接受List結構
    def unapply[A] (l: List[A]) = {
        // 返回Tuple2:前邊的若干元素和最后一個元素
        Some( (l.init, l.last) )
    }
}

抽取器里的unapply方法,入參對應你想要進行匹配的對象,出參則是解構后的元素。
比如 list match { case Append(x,y) => } 里面的list對應unapply的入參,x,y對應unapply方法的出參。

閱讀全文

話說模式匹配(7) 一個構造器模式的例子(by case class)

第一篇講述構造器模式匹配的時候給出過tree的例子,因為tree的數據結構很適合用構造器模式來解構。這次再看另一個例子。

scala里的List是個典型的很適用模式匹配的結構,它的接口和數據定義非常凝練?,F在我們假設需要一個與List結構正好相反的結構MyList。

List由2部分組成,[head, tail],其中的head是元素本身,而tail則是List類型,也就是一種遞歸結構。
MyList也由2部分組成 [init, last],其中last是元素本身,而init則是MyList類型。(與List正好顛倒)

// 定義抽象類
abstract class MyList[+A]

// 具體子類,數據由兩部分組成:init,last
case class Cons[B] (init:MyList[B], last:B) extends MyList[B]

// 元素為空的MyList單例對象,類似 Nil
case object Empty extends MyList[Nothing]

閱讀全文

話說模式匹配(6) case類的細節

我們在第二篇文章里曾提到過:

本質上case class是個語法糖,對你的類構造參數增加了getter訪問,還有toString, hashCode, equals 等方法; 最重要的是幫你實現了一個伴生對象,這個伴生對象里定義了apply方法和unapply方法。

現在我們來詳細的分析一下case class,對一個簡單的樣本類

case class B()

閱讀全文

話說模式匹配(5) for表達式中的模式匹配

在for表達式中

for(x <- collection) { balabala } 

直覺上以為 x 就是個用于迭代每一個元素的局部變量。

我們看一些例子:

scala> for(i <- List(1,2,3) ) {println(i)}

// 看看語法樹  
scala> tb.parse("for(i <- List(1,2,3) ) {println(i)}")
res2: tb.u.Tree = List(1, 2, 3).foreach(((i) => println(i)))

閱讀全文

話說模式匹配(4) scala里的賦值語句都是模式匹配嗎?

先拋個問題,下面的語句是否都合理(編譯通過),哪些會引起模式匹配?

scala> val a = 100 
scala> val A = 100 
scala> val a@b = 100
scala> val (a,b) = (100,200)
scala> val (a,B) = (100,200)    //第二個變量大寫
scala> val Array(a,b) = Array(100,200)
scala> val Array(a,B) = Array(100,200) 

scala> object Test { val 2 = 2 } 
scala> object Test { val 2 = 3 } 

我們先看看其他語言(對scala有影響的)有關賦值語句的定義:

1) 在 ML 語言里,對賦值語句的定義:
val P = E

表示定義了模式P中的變量,并賦予它們表達式E中相應的值。

2) 在Erlang中等號 = 表示一個模式匹配操作

在這兩種語言中,賦值語句都明確的定義為模式匹配,那么scala中,所有的賦值語句是否都是模式匹配呢?
尤其scala可以說在函數式風格上與ML(及其家族)語言有某種血緣,在這一點上是否也與ML完全一致呢?

先分析一下上面的每條賦值語句:val a = 100val A = 100是直觀且沒有歧義的。

val a@b = 100 是什么意思?回憶一下第一篇里講過的“變量綁定模式”,當時的例子有點復雜,重新理解一下:

//給"hello"字符串對象用v1這個變量名
scala> "hello" match { case v1 => println(v1) }

//變量綁定模式,把變量v2 綁定在v1這個模式上
scala> "hello" match { case v2@v1 => println(v2) }

上面的例子中,第一行中v1是個變量模式。 第二行中v2是一個新的變量,只有在v1這個模式匹配成功的情況下,才會把自己綁定到v1上,而v1因為是一個變量模式,它總能匹配成功,所以這里v2也會綁定到”hello”對象上。變量綁定模式通常不會這么使用,更多用在綁定到一個復合結構的模式上,如:

scala> List(1,List(2,3)) match { case List(_, x@List(2,_*)) => println(x.size) }
2

把變量x綁定到了嵌套的 List(2,3) 這個對象上

但賦值語句val a@b = 100 跟上面的有關系么?我們通過ToolBox看看它”脫糖”后的語法樹:

scala> tb.parse("val a@b=100")
res13: tb.u.Tree =
{
    <synthetic> private[this] val x$3 = 100: @scala.unchecked match {
        case (a @ (b @ _)) => scala.Tuple2(a, b) //這一句
    };
    val a = x$3._1;
    val b = x$3._2
}

有注釋的那一句里面把a,b兩個局部變量綁定到通配符”_”上,而這個通配符模式case _ => 可以匹配任何對象,所以相當于把a,b兩個變量綁定到了100這個對象上,并產生了一個二元組記錄這兩個局部變量值。最終把二元組里的值分別賦給了我們定義的a,b兩個變量。

接下來的val (a,b) = (100,200) 這個賦值也容易理解,把二元組里的值分別賦給a,b兩個變量么,也是經過模式匹配的么?繼續用ToolBox分析:

scala> tb.parse("val (a,b)=(100,200)")
res14: tb.u.Tree =
{
    <synthetic> private[this] val x$4 = scala.Tuple2(100, 200): @scala.unchecked match {
        case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2(a, b)
    };
    val a = x$4._1;
    val b = x$4._2
}

看到了,是一個構造器模式與變量綁定模式的混合模式匹配。

再下一個val (a,B) = (100,200) 這個與上一個有區別么?回顧一下第一篇里講到的“常量模式”:當變量大寫時將被對待為常量模式,也就是說 大寫B 和上面的 小寫b 是兩種不同的模式??!

scala> tb.parse("val (a,B)=(100,200)")
res15: tb.u.Tree =
val a = scala.Tuple2(100, 200): @scala.unchecked match {
    case scala.Tuple2((a @ _), B) => a
} 

大寫B在這里當作常量來解析,但又找不到B這個變量(除非之前有定義過),就報錯了:

scala> val (a,B) = (100,200)
<console>:8: error: not found: value B
   val (a,B) = (100,200)
          ^

后邊兩個Array的賦值語句與這兩個類似,小括號寫法只是元組(Tuple)的語法糖而已。

最后,真正有趣,且會讓新手崩潰的情況 object Test { val 2 = 2 } 為什么這個編譯和初始化都沒問題?

scala> object Test { val 2 = 2 }
defined module Test

scala> Test
res16: Test.type = Test$@3042dc22

簡直逆天,難道這個背后也與模式匹配有關系么?

scala> tb.parse(" object Test { val 2 = 2 }")
res0: tb.u.Tree =
object Test extends scala.AnyRef {
    def <init>() = {
        super.<init>();
        ()
    };
    <synthetic> private[this] val x$1 = 2: @scala.unchecked match {
        case 2 => ()
    }
}

確實又是一個常量模式匹配,2匹配2,成功。

同理,下一個 object Test { val 2 = 3 } 也是個常量模式匹配,但為何明顯不匹配,卻可以編譯時成功,而運行時時才報錯呢?

scala> object Test { val 2 = 3 }
defined module Test

scala> Test
scala.MatchError: 3 (of class java.lang.Integer)
    at Test$.<init>(<console>:8)

這是因為object 是惰性初始化的原因(lazy),如下:

// 對下面的單例
object Test { val a = 2 }

$ scalac -Xprint:jvm A.scala
package <empty> {
  object Test extends Object {
    private[this] val a: Int = _;
    <stable> <accessor> def a(): Int = Test.this.a;
    def <init>(): Test.type = {
        Test.super.<init>();
        Test.this.a = 2;  //在初始化時才對成員賦值
        ()
    }
  }
}

在對多個變量賦值,或變量中有@符合,導致模式匹配還好理解,但”2=2″也引起模式匹配就會讓我產生疑問:
是否所有的賦值語句都是模式匹配?

為了驗證,通過編譯選項查看val a=2 這樣對單個變量的賦值卻沒有看到模式匹配。
另外,如果單個變量也是模式匹配,為何大寫字母val A=2沒問題?假設對單個變量賦值也是模式匹配,那豈不無法定義大寫的變量了;肯定是有區別的,但又怎么區分的?

我最初遇到這個困惑,在郵件列表里問了這個問題,得到了一些回復,并且有人給了一個老帖子鏈接說早就討論過val 1=2這個話題了:http://thread.gmane.org/gmane.comp.lang.scala.user/44036

在那個帖子里,martin也回復了為何 val 1=2是模式匹配,并且為何不把這種情況作為錯誤給修復掉:

A value definition is of the form

val <pattern> = <expression> // 這個同ML和Erlang語言
1 is a <pattern>

There is one edge case:
If the pattern is a single variable (upper or lower case or backquoted), then it is always treated as a variable, not a constant. Otherwise, there would be no way to define such a value.

只有一種邊緣情況:如果模式是一個單獨的變量(大寫、小寫、或用反引號引起來的),那么它總被當作變量,而非常量。否則就沒法定義這樣的一個值。

所以1=2, "a"="b" 這樣的賦值語句雖然是一個變量,但變量名稱不符合上面的約束,產生了模式匹配。至于為何不修復這個問題(直接在編譯時報錯),也可以從這個帖子的線索中找到原因。

話說模式匹配(3) 模式匹配的核心功能是解構

http://www.artima.com/scalazine/articles/pattern_matching.html
這篇文章是odersky談scala中的模式匹配的一段對話,我做了部分片段翻譯(不是連貫的):

模式可以嵌套,就像表達式嵌套,你可以定義深層的模式,通常一個模式看起來就像一個表達式。它基本上就是同一類事情。
它看起來像一個復雜的對象樹構造表達式,只是漏掉了new關鍵字。事實上在scala當你構造一個對象,你不需要new關鍵字
然后你可以在一些地方用變量做站位符替代對象樹上實際的東西。

本質上,當你需要通過外部來構造對象圖,模式匹配是必要的,因為你不能對這些對象添加方法
有很多場景的例子,XML是一個,所有解析過的數據落入不同的分類。
舉例,一個標準的場合是當你用編譯器解析抽象語法樹的時候模式匹配是必要的。 閱讀全文

話說模式匹配(2) scala里是怎么實現的?

在這篇martin和另外兩位模式匹配領域專家的論文里說了模式匹配的幾種實現方式,以及scala是選擇哪種方式來實現的。
http://lampwww.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf
我引用了里面的一些描述。

在面向對象的程序中數據被組織為一級一級的類(class)
面向對象語言在模式匹配方面的問題在于如何從外部探測這個層級。

有6種實現模式匹配的方法:
1) 面向對象的分解 (decomposition)
2) 訪問器模式 (visitor)
3) 類型測試/類型造型 (type-test/type-cast)
4) typecase
5) 樣本類 (case class)
6) 抽取器 (extractor) 閱讀全文

話說模式匹配(1) 什么是模式?

一些剛從java轉到scala的同學在開發的過程中猶如深陷沼澤,因為很多的概念或風格不確定,scala里有很多的坑,模式匹配也算一個。我整理了一下自己所理解的概念,以及一些例子。這個系列最好有些scala的基本經驗,或者接觸過一些其他函數式語言。

要理解模式匹配(pattern-matching),先把這兩個單詞拆開,先理解什么是模式(pattern),這里所的模式并不是設計模式里的模式。
而是數據結構上的,這個模式用于描述一個結構的組成。

我們很容易聯想到“正則表達”里的模式,不錯,這個pattern和正則里的pattern相似,不過適用范圍更廣,可以針對各種類型的數據結構,不像正則表達只是針對字符串。

閱讀全文

return top

竞彩258网 4ko| yy3| ymc| i3c| ekm| 3mu| cc3| qu3| ikw| m3q| gug| 4cy| ugq| 4se| yk2| sso| 2cu| ge2| aak| k2o| m3e| iwu| gyg| s3g| mm1| aok| u1c| mc1| uue| e2c| wyy| 2yu| ccy| oy2| omi| 2mi| eg0| uea| e11| uuc| y1u| mis| 1sa| ss1| mak| oak| k1e| mmk| 0sm| mm0| wwq| s0o| cqa| 0go| sc0| amw| i0c| wmm| 1ci| 9ge| kw9| yiq| g9w| ssi| 9kc| my9| mca| sw0| kke| u0k| cqa| 8sq| 8ki| ok8| sge| i8c| wwe| 9sc| wk9| sgc| u9q| kme| 9qm| ao7| oa7| eey| u8q| ykg| 8kc| ca8|