1. 程式人生 > >d3.js+react實現演算法視覺化:排序篇

d3.js+react實現演算法視覺化:排序篇

前言:

知道d3.js已經很長時間了,直到去年才好好看了一些教程寫了一些demo,當時還是3.x版。最近這幾天在看react、react-router之類的,想自己寫些東西出來,剛好想到該複習下d3.js,自己就做了這個演算法視覺化的demo,目前只做了排序,包括氣泡排序、插入排序、選擇排序、歸併排序、快速排序,之後有時間的話會繼續做其他的資料結構與演算法的視覺化。

目前有很多演算法視覺化的網站,比如新加坡的National University of Singapore就有個visualgo:點選開啟連結

visualgo的演算法已經很齊全了,而且介面、互動都很不錯,比起我的demo那就強太多了安靜


我的demo演示地址:點選開啟連結(起了個山寨味很濃的名字,VisualCompute)

使用npm+jspm搭建工程,使用react-router + d3.js 操作svg影象,順便使用了Materialize當UI(關於select控制元件有個坑,onChange事件獲取不到)。

基本思路:

由於react建議不直接操作dom,所以d3的選擇器也就用不上了,svg都是由react進行render,react的方便之處在於每次更新state後會重新render一下dom,所以在排序的時候,每當資料發生變化,就更新state。d3的優勢就在於可以進行比例變換,根據資料的大小,把svg的高度變換的一個指定的範圍,這樣免得圖形過大或過小。而且d3還能生成動畫,不過我這裡暫時沒用上。

所以比較關鍵的地方就在於,如何更新state?

對於一個排序演算法,比如氣泡排序,每次資料發生變化時怎麼樣通知外界呢?通過回撥函式?但是在演算法視覺化過程中要有能力暫停演算法,然後更新dom,之後再繼續執行演算法(比如要支援使用者手動點選“下一步”),所以我覺得純粹使用callback似乎有些麻煩。剛開始想的是儲存每一步中間結果,這樣不僅可以讓演算法“下一步”,還能“上一步”觀察結果,不過後來突然想到es6的generator,generator可以生成迭代器啊!而且還把控制權限交給了外界!有外界決定這個generator是否要執行下一步,感覺很適合。由於之前一直沒有用過generator,想想還是趁此機會用下吧。

ES6 Generator

基本語法

  • 使用function*(){}定義,返回的是一個迭代器,函式內部使用yield產出資料,每次迭代器呼叫next會獲得下一個yield產出的資料
function* Generator(){
    yield 1;
    let v = yield 2;
    yield v;
}

var iterator = Generator();
console.log(iterator);//輸出{},只有呼叫next後才有資料
console.log(iterator.next());//{ value: 1, done: false }
console.log(iterator.next());//{ value: 2, done: false }
console.log(iterator.next());//{ value: undefined, done: false }, yield並不能把值傳給v,yield*+generator可以
console.log(iterator.next());//{ value: undefined, done: true }
  • 使用yield*可以產出迭代器
function* Generator1(){
    yield 1;
    let v = yield* Generator2();//這裡使用的是yield*,會把Generator2的控制器接入,如果使用yield,則還需要呼叫next一次後才能得到迭代器
    yield v;
}
function* Generator2(){
    yield 2;
    return 3;//這裡的return會傳給Generator1中的v
}
var iterator = Generator1();
console.log(iterator.next());//{ value: 1, done: false }
console.log(iterator.next());//{ value: 1, done: false }
console.log(iterator.next());//{ value: 3, done: false }
console.log(iterator.next());//{ value: undefined, done: true }
下面就以氣泡排序來說明下demo中的實現

Generator+氣泡排序

  • 氣泡排序JS實現:
var data = [
    {v:5,color:"blue"},
    {v:6,color:"blue"},
    {v:2,color:"blue"},
    {v:0,color:"blue"},
    {v:7,color:"blue"},
    {v:3,color:"blue"},
    {v:9,color:"blue"}
];//這裡定義一些svg柱狀圖的引數,包括高度和顏色兩種屬性

function bubbleSort(data){
    var length = data.length;
    for(var i=0;i<length;i++){
        for(var j=0;j<length-1;j++){
            if(data[j].v>data[j+1].v){
                //交換
                let t = data[j].v;
                data[j].v = data[j+1].v;
                data[j+1].v = t;
            }
        }
    }
}
bubbleSort(data);
console.log(data.map(v=>v.v));//[ 0, 2, 3, 5, 6, 7, 9 ]

  • generator+冒泡
function* bubbleSort(data){
    var length = data.length;
    for(var i=0;i<length;i++){
        for(var j=0;j<length-1;j++){
            data[j].color = colorYellow;//把當前這步的資料標黃
            yield data;//產出資料
            if(data[j].v>data[j+1].v){
                //交換
                data[j].color = data[j+1].color = colorRed;//把要交換的資料標紅
                yield data;//產出資料
                let t = data[j].v;
                data[j].v = data[j+1].v;
                data[j+1].v = t;
                yield data;//產出交換位置後的資料
            }
            resetColor(data);//重置資料color顏色屬性
        }
    }
}

這個generator版的氣泡排序可以有外界呼叫next方法進行下一步運算,而且會把data返回出來,由於data中的一些資料發生變化,這時候通過react的setstate方法可以實現dom的更新,呼叫示例如下:

 /**********處理各種演算法************/
        var iter = bubbleSort(data);
        var go = function() {
            let currentData = iter.next();
            if(!currentData.done){
		//更新state
                this.setState( (prevState) => {return {"data":currentData.value}});
                setTimeout(go,500);
            }
        }.bind(this);
        setTimeout(go,0);


冒泡的最後效果如下:


其他排序都是差不多的思路,只不過在快速排序和歸併排序中用到了yield* 。

 原始碼

demo的原始碼可以在本文開頭提到的連線上覆制下來,也可以在這裡找到:https://git.oschina.net/liuyaqi/VisualCompute

要使用npm install和jspm install

demo演示地址:點選開啟連結