2017-09-07 43 views
0

努力學習遞歸,不太明白範圍

function isNumber(arr) { 
 
    var result = []; 
 
    if (arr.length > 1) { 
 
     if (typeof arr[0] === 'number') { 
 
      result.push(arr[0]); 
 
      arr.splice(0, 1); 
 
      isNumber(arr); 
 
     } else { 
 
      arr.splice(0, 1); 
 
      isNumber(arr); 
 
     } 
 
    } else { 
 
     console.log(result); 
 
    } 
 

 
} 
 

 
isNumber([1,'2','3',2]);

我試圖創建一個通過一個數組,如果第一項是一個數字,你會刪除它,推去功能它被稱爲結果數組。然後它再次調用自己,直到數組爲空。

它當前返回一個空數組,並且調試器顯示結果數組保持重置爲空數組。

我明白爲什麼發生這種情況,但我想弄清楚如何解決它。

+0

每次調用,你鬆的結果,你不回來。 –

+2

您必須將您的結果數組作爲參數傳遞。你'arr.length> 1'應該是'arr.lenght> 0' –

+2

這個例子真正的問題是,它是沒有意義的使用遞歸此。一個簡單的for循環會很好。 –

回答

1

您可以用兩種不同的方式做到這一點。這其中需要攜帶結果數組和你在一起,但也可以是尾調用優化:

function onlyNumbers(arr, result) { 
    if (typeof result === "undefined") result = []; 
    if (arr.length == 0) return result; 
    let element = arr.shift(); 
    if (typeof element === "number") { 
    result.push(element); 
    } 
    return onlyNumbers(arr, result); 
} 

我用了一招這裏初始化只在第一次調用的結果陣列。在更嚴格的語言中,我會使用兩種不同的函數:一種(非遞歸的)只是用(arr, [])來調用另一個函數。在第一次循環之後,結果數組隨着每次調用而結轉,每次遞歸調用都被填充,然後調用堆棧被解除,result返回而沒有任何改變。

相反的是這種模式的真正的(更簡單的概念,因爲你並不需要額外的參數,但不能被優化好):

function onlyNumbers(arr) { 
    if (arr.length == 0) return []; 
    let element = arr.shift(); 
    let result = onlyNumbers(arr); 
    if (typeof element === "number") { 
    result.unshift(element); 
    } 
    return result; 
} 

在這裏,我們不preconstruct結果數組,而是在堆棧展開時將結果元素(向後!)附加到返回值。

當然,遞歸是函數式編程的基礎。在許多函數式編程語言中,沒有其他方法可以循環,只能遞歸。但是,這並不意味着遞歸仍然是原始操作:通常這些語言將基於遞歸構建其他更復雜的操作,然後使用它們而不是裸遞歸來產生很大效果。其中最基本的是map,filterreduce

一旦你理解了遞歸代碼是如何工作的,你可能想看看如何filter通過遞歸來實現,以及如何在JavaScript中可以這樣使用更簡單的一行:

[1,'2','3',2].filter(x => typeof x === "number") 
# => [1, 2] 
+0

謝謝Amadan!你會碰巧知道我可以學習函數式編程的任何好資源嗎?我已經嘗試過Youtube並且它非常有限。 – jonathanpuc

+1

遞歸的好處是避免存儲在局部變量中的中間值,只使用調用棧。你的方法都不能避免它們。 – ftor

1

結果應的功能之外創建的,因爲現在每個函數調用創建一個新的空的結果,並使用新的,而不是舊的,那麼你就可以檢查功能工作納入它

var result = []; 
    function isNumber(arr) {    
     if (arr.length > 1) { 
      if (typeof arr[0] === 'number') { 
       result.push(arr[0]); 
       arr.splice(0, 1); 
       isNumber(arr); 
      } else { 
       arr.splice(0, 1); 
       isNumber(arr); 
      } 
     } else { 
      console.log(result); 
     } 

    } 

    isNumber([1,'2','3',2]); 
    console.log(result); 

如果你想代替,返回與結果數組中的功能,你應該通過數組作爲參數的函數,然後在函數調用

function isNumber(arr, result) {    
     if (arr.length > 1) { 
      if (typeof arr[0] === 'number') { 
       result.push(arr[0],result); 
       arr.splice(0, 1); 
       isNumber(arr); 
      } else { 
       arr.splice(0, 1); 
       isNumber(arr,result); 
      } 
     } 

    } 

    var result=[]; 
    isNumber([1,'2','3',2], result); 

    console.log(result); 
1

您需要存儲結果並將其返回後檢查。

function isNumber(arr) { 
 
    var result = typeof arr[0] === 'number' ? [arr[0]] : []; 
 
    arr.shift(); 
 
    return arr.length ? result.concat(isNumber(arr)) : result; 
 
} 
 

 
console.log(isNumber([1, '2', '3', 2])); 
 
console.log(isNumber([]));

1

最小的變化你的代碼:

function isNumber(arr, result) { 
 
    if (arr.length > 0) { 
 
     if (typeof arr[0] === 'number') { 
 
      result.push(arr[0]); 
 
      arr.splice(0, 1); 
 
      return isNumber(arr, result); 
 
     } else { 
 
      arr.splice(0, 1); 
 
      return isNumber(arr, result); 
 
     } 
 
    } else { 
 
     console.log(result); 
 
    } 
 

 
} 
 

 
isNumber([1,'2','3',2], []);

0

它沒有任何意義,使用遞歸這件事。如果你真的想這樣做,那麼下面的例子就是這樣做的一種方式。

您需要的結果數組每次調用該函數的新變量存儲陣列中,因此它可以在年底返回的時間通過。在你現在的例子中,它一直覆蓋變量。

function isNumber(arr, res) { 
 
    // If there is no res set, create a new array 
 
    var result = res || []; 
 
    
 
    // Base case, return the result if the arr is empty 
 
    if (arr.length <= 0) { 
 
    console.log(result); 
 
    return result; 
 
    } 
 
    
 
    // Get the first element from the array (and remove it from the array) 
 
    var firstElement = arr.shift(); 
 

 
    // If it's a number, add it to the array 
 
    if (typeof firstElement === 'number') { 
 
    result.push(firstElement); 
 
    } 
 
    
 
    isNumber(arr, result); 
 
} 
 

 
isNumber([1, '2', '3', 2]);

+0

嗯,它的確如此。如果你在一般情況下考慮它,過濾數據結構非常適合遞歸方案。 –

+0

當然,我的意思是在這種情況下具體。使用過濾器可以更容易地實現結果,如:[1,'2','3',2] .filter(function(el){return typeof el ==='number';});' – Patrick2607

+1

OP正試圖學習遞歸。過濾數組(儘管有一個內置的方法可以實現)是一個非常有效的學習示例。即使計算數組長度的遞歸方式也可以學習,儘管有'數組'。長度' –

2

要理解遞歸,你必須理解遞歸:)

  • 定義終端條件(array.length爲0)
  • 定義步驟(你對當前步驟做)你如何更深入。

function isNumber(arr) { 
 
    // terminate recursion 
 
    if (arr.length === 0) return [] 
 

 
    // step 
 
    var first = arr[0], 
 
    rest = arr.slice(1) 
 

 
    // if first is a number concat it with the rest else next step 
 
    return typeof first === 'number'? [first].concat(isNumber(rest)) : isNumber(rest) 
 
} 
 

 
console.log(isNumber([1, '2', '3', 2]));