Read Sean

          Read me, read Sean.
          posts - 508, comments - 655, trackbacks - 9, articles - 4

          用Scala解Hanoi塔

          Posted on 2009-11-23 19:20 laogao 閱讀(2618) 評論(3)  編輯  收藏 所屬分類: On JavaProgramming in GeneralOther Languages

          到處吹噓Scala很長時間了,卻一直沒有在自己的blog上增加過任何相關內容,今天就來補一補。當然,Scala的基本特性就不多廢話了,網上已經有相當多的資料,如果懶得google,只想看完本文,那么你只需要知道它是一門以JVM為目標運行環境的靜態編程語言(當然,Scala官方也有.NET版,但已不是其重點),同時具備面向對象和函數式編程語言的特點。本文將通過一個簡單的示例,展示Scala的FP能力,其中十分heavy的用到了implicit隱式轉換和模式匹配。

          代碼是從guithub的gist上抄的,簡單改了改,原始代碼見: http://gist.github.com/66925

          import scala.reflect.Manifest
            
          def simpleName(m:Manifest[_]):String 
          = {
            val name 
          = m.toString
            name.substring(name.lastIndexOf(
          '$')+1)
          }
            
          trait Num
          final class Succ[Pre<:Num] extends Num
          final class _1 extends Num
          type _2 
          = Succ[_1]
          type _3 
          = Succ[_2]
          type _4 
          = Succ[_3]
           
          case class Move[N<:Num,A,B,C]()

          implicit def move1[A,B,C](implicit a:Manifest[A], b:Manifest[B]) : Move[_1,A,B,C] 
          = {
            System.out.println(
          "Move from " + simpleName(a) + " to " + simpleName(b))
            
          null
          }
           
          implicit def moveN[P
          <:Num,A,B,C](implicit m1:Move[P,A,C,B], m2:Move[_1,A,B,C], m3:Move[P,C,B,A]) : Move[Succ[P],A,B,C] = null
            
          def run[N
          <:Num,A,B,C](implicit m:Move[N,A,B,C]) = null
            
          case class Left()
          case class Center()
          case class Right()
            
          run[_3,Left,Right,Center]

           

          這段代碼解決的是經典的漢諾塔問題,通過一根中柱,將左柱上64個大小依次疊加的圓盤全部移動到右柱,要求整個過程中每次只能移動一個圓盤,且每個圓盤只能獨立占據一根柱子或是疊加在比自身更大的圓盤上。

          簡單分析一下就知道,這是一個遞歸問題(FP的英雄特長):

          • 當只有1個圓盤時,不用通過中柱,直接可以從左柱移動到右柱;
          • 當有2個圓盤時,將小盤移動到中柱,剩下的大盤移動到右柱,再從中柱把小盤移動到右柱;
          • 當有3個圓盤時,先移動2個圓盤到中柱,再移動大盤到右柱,再移動2個圓盤到右柱;
          • ...

          很容易發現一個pattern,那就是移動N(N>1)個圓盤,可以通過以下三個步驟:

          1. 移動N-1個圓盤,從左柱到中柱;
          2. 移動剩下的1個圓盤,從左柱到右柱;
          3. 移動N-1個圓盤,從中柱到右柱。

          在解釋代碼之前,先說說Scala的implicit隱式轉換,這是一個非常powerful的idea,當Scala編譯器發現類型不匹配,它不會直接fail,而是嘗試從代碼中指定的,在scope內的implicit轉換定義,來替換問題對象或表達式以滿足類型要求,當然,為了避免歧義,同一時刻Scala需要找到唯一的一個滿足條件的implicit定義。

          我們的代碼首先定義了一個取得友好類名的方法,不去深究它,然后定義了一個正整數的序列,也不去深究它了,你只需要當作他們是正整數就好,接觸過FP的同學應該對此類定義不陌生,接下來定義了如下3個支持implicit傳入參數的方法:

          1. implicit def move1[A,B,C](implicit a:Manifest[A], b:Manifest[B]) : Move[_1,A,B,C]
          2. implicit def moveN[P<:Num,A,B,C]( implicit
            m1:Move[P,A,C,B],
            m2:Move[_1,A,B,C],
            m3:Move[P,C,B,A]
            ) : Move[Succ[P],A,B,C]
          3. def run[N<:Num,A,B,C](implicit m:Move[N,A,B,C])

          含義分別是:

          1. 當需要Move[_1,A,B,C]值時,可以找我幫忙(我有個side-effect,那就是輸出移動指令);
          2. 當需要Move[Succ[P],A,B,C]時,可以找我幫忙;
          3. 運行我,我接受Move[N,A,B,C]類型的參數。

          簡單說明:Scala用[]表示類型參數,區別于Java的<>,另外,Scala的類型聲明在變量/函數定義之后。Move[_,A,B,C]的含義是通過C,從A移動圓盤到B。

          我們來模擬運行一下,為了演示效果,用一個中等復雜的數目,3個圓盤,從Left移動到Right。

          run[_3,Left,Right,Center],對應Move[Succ[_2],Left,Right,Center],于是展開成三個Move:

          Move[_2,Left,Center,Right]即Move[Succ[_1],Left,Center,Right]
          Move[_1,Left,Right,Center]
          Move[_2,Center,Right,Left]即Move[Succ[_1],Center,Right,Left]

          然后繼續展開Move[_2,Left,Center,Right]和Move[_2,Center,Right,Left],得到:

          Move[_1,Left,Right,Center]
          Move[_1,Left,Center,Right]
          Move[_1,Right,Center,Left]
          --------------------------
          Move[_1,Left,Right,Center]
          --------------------------
          Move[_1,Center,Left,Right]
          Move[_1,Center,Right,Left]
          Move[_1,Left,Right,Center]

          這個時候已經全部都匹配Move[_1,A,B,C],于是我們很容易得到以下輸出:

          Move from Left to Right
          Move from Left to Center
          Move from Right to Center
          Move from Left to Right
          Move from Center to Left
          Move from Center to Right
          Move from Left to Right

          希望本文能帶給Scala初學者一些感性認識和啟發。


          Feedback

          # re: 用Scala解Hanoi塔  回復  更多評論   

          2009-11-24 16:28 by 大胃
          歡迎大家參與討論,我會不定期刪除自言自語式的、軟廣告式的、和主題無關的、沒有營養的回復。

          # re: 用Scala解Hanoi塔  回復  更多評論   

          2010-06-11 16:18 by 大胃
          新發表一篇文章,歡迎前往討論
          http://laogao.me/blog/view/8

          # re: 用Scala解Hanoi塔  回復  更多評論   

          2012-05-18 23:14 by Freewind
          我謹代表群內171位scalaer,熱切希望你加入到scala熱情交流群: 132569382(QQ群)
          主站蜘蛛池模板: 盈江县| 秦安县| 通辽市| 上蔡县| 海安县| 涿鹿县| 柳江县| 太保市| 句容市| 郎溪县| 鹤峰县| 寿宁县| 平阴县| 安乡县| 花垣县| 上蔡县| 新邵县| 濉溪县| 城市| 呼图壁县| 巧家县| 咸丰县| 湘西| 惠安县| 元江| 蕉岭县| 玉田县| 子洲县| 稷山县| 湘潭县| 比如县| 阿拉善右旗| 佛冈县| 江川县| 东平县| 洪湖市| 九龙坡区| 紫云| 榆中县| 南溪县| 贡觉县|