1. 程式人生 > 實用技巧 >PAT甲級1151LCA in a Binary Tree

PAT甲級1151LCA in a Binary Tree

1、什麼是Generator函式

在javascript中,一個函式一旦開始執行,就會執行到最後或者遇到return時結束,執行期間不會有其他程式碼能夠打斷它,也不能從外部再傳入值到函式體內

而Generator函式(生成器)的出現使得打破函式的完整執行成為了可能,其語法行為與傳統函式完全不同,Generator函式是es6提供的一種非同步程式設計解決方案,形式上也是一個普通函式,但是有幾個顯著的特徵:

---function關鍵字與函式名之間有一個*

---函式體內使用yield表示式,定義不同的內部狀態(可以有多個yield)

---直接呼叫generator函式並不會執行,也不會返回執行結果,而是返回一個遍歷器物件

---依次呼叫遍歷器物件的next方法,遍歷generator內部的每一個狀態

 1 {
 2   // 傳統函式
 3   function foo() {
 4     return 'hello world'
 5   }
 6 
 7   foo()   // 'hello world',一旦呼叫立即執行
 8 
 9 
10   // Generator函式
11   function* generator() {
12     yield 'status one'         // yield 表示式是暫停執行的標記  
13     return 'hello world'
14   }
15 16 let iterator = generator() // 呼叫 Generator函式,函式並沒有執行,返回的是一個Iterator物件 17 iterator.next() // {value: "status one", done: false},value 表示返回值,done 表示遍歷還沒有結束 18 iterator.next() // {value: "hello world", done: true},value 表示返回值,done 表示遍歷結束 19 }

上面程式碼中可以看到傳統函式和generator函式的執行是完全不同的,

傳統函式呼叫後立即執行並輸出了返回值;

generator函式則沒有執行而是返回一個iterator物件,並通過呼叫iterator物件的next方法來遍歷,函式體內的執行看起來更像是“被人踢一腳才動一下”的感覺

 1 {
 2   function* gen() {
 3     yield 'hello'
 4     yield 'world'
 5     return 'ending'
 6   }
 7 
 8   let it = gen()
 9 
10   it.next()   // {value: "hello", done: false}
11   it.next()   // {value: "world", done: false}
12   it.next()   // {value: "ending", done: true}
13   it.next()   // {value: undefined, done: true}
14 }

上面程式碼中定義了一個generator函式,其中包括2個yield表示式和一個return語句(即產生了三個狀態)

每次呼叫iterator物件的next方法時,內部的指標就會從函式的頭部或者上一次停下來的地方開始執行,知道遇到下一個yield表示式或者return語句暫停。換句話說,generator函式是分段執行的,yield表示式是暫停執行的標記,而next方法可以恢復執行

執行過程如下:

第一次呼叫next方法時,內部指標從函式頭部開始執行,遇到第一個yield表示式暫停,並返回當前狀態的值“hello”

第二次呼叫next方法時,內部指標從上一個(即第一個)yield表示式開始,遇到第二個yield表示式暫停,返回當前狀態的值“world”

第三次呼叫next方法時,內部指標從第二個yield表示式開始,遇到return語句暫停,返回當前狀態的值“ending”,同時所有狀態遍歷完畢,done屬性的值變為true

第四次呼叫next方法時,由於函式已經遍歷執行完畢,不再有其他狀態,因此返回{value:undefined,done:true}.如果繼續呼叫next方法,返回的也是都是這個值

2、yied表示式

(1)yield表示式只能用在generator函式裡面,用在其他地方都會報錯

(2)yield表示式如果用在另一個表示式中,必須放在圓括號裡面

1 {
2   function* demo() {
3     console.log('Hello' + yield); // SyntaxError
4     console.log('Hello' + yield 123); // SyntaxError
5   
6     console.log('Hello' + (yield)); // OK
7     console.log('Hello' + (yield 123)); // OK
8   }
9 }

(3)yield表示式用作引數或放在賦值表示式的右邊,可以不加括號

1 {
2   function* demo() {
3     foo(yield 'a', yield 'b'); // OK
4     let input = yield; // OK
5   }
6 }

(4)yield表示式和return語句的區別

相似:都能返回緊跟在語句後面的那個表示式的值

區別:---每次遇到yield,函式就暫停一下,下一次再從該位置繼續向後執行;而return語句不具備記憶位置的功能

---一個函式只能執行一次return語句,而在generator函式中可以有任意多個yield

3、yield*表示式

如果在generator函式裡面呼叫另一個generator函式,預設情況下是沒有效果的

 1 {
 2   function* foo() {
 3     yield 'aaa'
 4     yield 'bbb'
 5   }
 6 
 7   function* bar() {
 8     foo()
 9     yield 'ccc'
10     yield 'ddd'
11   }
12 
13   let iterator = bar()
14 
15   for(let value of iterator) {
16     console.log(value)
17   }
18 
19   // ccc
20   // ddd
21 
22 }

上例中,使用for...of遍歷函式bar生成的遍歷器物件時,值返回了bar自身的兩個狀態值。此時,如果想要正確的在bar裡呼叫foo,就需要用到yield*表示式

yield*表示式用來在一個generator函式裡面執行另一個generator函式

 1 {
 2   function* foo() {
 3     yield 'aaa'
 4     yield 'bbb'
 5   }
 6 
 7   function* bar() {
 8     yield* foo()      // 在bar函式中 **執行** foo函式
 9     yield 'ccc'
10     yield 'ddd'
11   }
12 
13   let iterator = bar()
14 
15   for(let value of iterator) {
16     console.log(value)
17   }
18 
19   // aaa
20   // bbb
21   // ccc
22   // ddd
23 }

4、next()方法的引數

yield表示式本身沒有返回值,或者說總是返回undefined。next方法可以帶一個引數,該引數就會被當作上一個yield表示式的返回值

1   [rv] = yield [expression]
2 
3   expression:定義通過遍歷器從生成器函式返回的值,如果省略,則返回 undefined
4   rv:接收從下一個 next() 方法傳遞來的引數
 1 {
 2   function* gen() {
 3     let result = yield 3 + 5 + 6
 4     console.log(result)
 5     yield result
 6   }
 7 
 8   let it = gen()
 9   console.log(it.next())      // {value: 14, done: false}
10   console.log(it.next())      // undefined    {value: undefined, done: false}
11 }

第一次呼叫遍歷物件的next方法,函式從頭部開始執行,遇到第一個yield暫停,在這個過程中其實是分了三步:

(1)申明一個變數result,並將申明提前,預設值為undefined

(2)由於generator函式是“惰性求值”,執行到第一個yield時才會計算求和,並加計算結果返回給遍歷器物件       {value:14,done:false},函式暫停執行