撲克牌排序-Scala之畫蛇添足版

海興發表於2012-11-30

老夫聊發少年狂,也來湊個寫程式碼的熱鬧。
搞大資料的幹啥都必須用上mapreduce,所以下面是畫蛇添足版的多執行緒實現。但對於排序問題,下面的程式碼過於複雜,切勿模仿。記住,把複雜的事情變簡單,是一種本事,把簡單的事情變複雜,是一種病,得治!

順便做個廣告,-> _ -> // -> _ -> // -> _ -> // -> _ -> // -> _ ->

設計思路

  • 牌的初始模型:
    • 元素數量為 4*13的集合,按順序隨機生成;
    • 根據每個數的取模結果區分花色。
  • 期望結果:
    • 集合中的元素分為4組,每組模4的結果分別為1,2,3,0;
    • 組內按由小到大的順序排列;
    • 每組第一個元素按由小到大的順序排列;
  • 排序的實現方式:
    • 由一個工頭actor把大陣列分成四個小陣列,四個工人actor對分給自己的小陣列進行排序
    • 子執行緒二分法遞迴排序
    • 等待子執行緒完成,主執行緒拼接各子執行緒排序結果

測試類

此處省略N行程式碼....

實現類

先定義actor之間通訊用的訊息類:

 sealed trait MapReduceMessage
 case class SortPorker(pokers: Iterator[Int]) extends MapReduceMessage
 case class SortSuit(suitList:List[Int]) extends MapReduceMessage
 case class Result(sortedSuit:Array[Int]) extends MapReduceMessage

SortPorker訊息是由測試類發給工頭actor的訊息,裡面是雜亂無序的52張撲克牌(無序的1~52)。SortSuit為工頭actor發給工人actor的訊息,根據取模結果發給對應的工人。每個工人actor在排序完成後都會返回一個Result。

下面是工頭actor:

  // 工頭Actor, 建立工人 Actors, 分配任務,收集結果
  class Master(nrOfWorkers: Int, latch:CountDownLatch) extends Actor {

    val workers = Vector.fill(nrOfWorkers)(actorOf[SortPokerWorker].start());
    val router = Routing.loadBalancerActor(CyclicIterator(workers)).start();

    val resultArray = new Array[Int](52);

    val suitSize = 13;

    var clubsList = List[Int]();
    var heartsList =List[Int]();
    var spadesList = List[Int]();
    var diamondsList = List[Int](); 

    var start : Long = _
    var count : Long = 0
    //對拿到的數值取模,判斷花色
    def splitSuit(poker:Int) = {
             poker % 4 match {
                     case 1 =>
                            clubsList = poker :: clubsList
                            sendSuitMessage(clubsList) 
                     case 2 => 
                            heartsList = poker :: heartsList
                            sendSuitMessage(heartsList )
                     case 3 =>
                            spadesList = poker :: spadesList 
                            sendSuitMessage(spadesList )

                     case 0 => 
                            diamondsList = poker :: diamondsList 
                            sendSuitMessage(diamondsList )
             } 
    }
    //判斷該花色的牌是否已全部拿到,全拿到則發給工人actor排序
    def sendSuitMessage(suitList:List[Int]) = {
           if(suitList.size == suitSize) {
                                 router ! SortSuit(suitList);
                                 count = count + 1;
           }
    }

    def receive = {
      case SortSuit(pokers: Iterator[Int]) =>
        //交給splitSuit取模,放入相應的列表
        pokers.foreach(splitSuit)

        /**
         * 不需要另外定義方法的簡易寫法
         * clubsList = pokers.filter(poker => poker % 4 == 1)
         * router ! sortSuit(clubsList);
         */

        //shutdown actors
        router ! Broadcast(PoisonPill)
        router ! PoisonPill

      case Result(values: Iterator[Int]) =>
        for (value <- values) {
          resultArray:+ value
        }
        count = count - 1;
        if (count <= 0) self.stop()
    }
  }

actor 類要實現Actor介面,其中有個receive方法。Routing.LoadbalancingActor是內建的actor,用於分發訊息,這裡用的是輪詢機制。 在收到SortPoker訊息後,工頭會對每個值取模,放到對應的列表中,放夠數的列表立馬發給工人actor進行排序。得到返回結果後,順序加到結果Array中。

工人actor如下所示:

class SortPoker extends Actor {

    def receive = {
       case  SortSuit(suitList) =>
         self reply Result(sort(suitList))
    }

    /*排序*/
    def sort(xs: Array[Int]):Array[Int] = {  
      if(xs.length <= 1)  
        xs;  
      else {  
        val pivot = xs(xs.length /2);  
        Array.concat(  
          sort(xs filter (pivot >)),  
             xs filter (pivot ==),  
          sort(xs filter (pivot < ))  
         )  
      }  
    }
  }

以上程式碼未經測試,可能無法執行,僅理論上 可行。

參考資料

1.用Actor實現MapReduce
2.Scala陣列排序的快速實現
3.Scala Tutorial – iteration, for expressions, yield, map, filter, count

相關文章