2017-05-05 31 views
1

使用累加器時,累加器僅存在於reduce塊中,還是存在於函數中?如何在Ruby中使用reduce/inject而不使用未定義的變量

我有一個看起來像的方法:

def my_useless_function(str) 
    crazy_letters = ['a','s','d','f','g','h'] 
    str.split.reduce([]) do |new_array, letter| 
    for a in 0..crazy_letters.length-1 
     if letter == crazy_letters[a] 
     new_array << letter 
     end 
    end 
    end 

    return true if (new_array == new_array.sort) 
end 

當我執行這個代碼中,我得到的錯誤

"undefined variable new_array in line 11 (the return statement)" 

我也試過new_array值內的else語句中賦值給另一個變量我的reduce塊,但這給了我相同的結果。

有人可以向我解釋爲什麼會發生這種情況嗎?

回答

2

塊局部變量

new_array沒有你reduce通話外塊存在。這是一個"block local variable"

reduce確實會返回一個對象,但您應該在您的方法中使用它。

sum = [1, 2, 3].reduce(0){ |acc, elem| acc + elem } 
puts sum 
# 6 
puts acc 
# undefined local variable or method `acc' for main:Object (NameError) 

您的代碼

這裏的變化對你的方法量最少的:

def my_useless_function(str) 
    crazy_letters = ['a','s','d','f','g','h'] 
    new_array = str.split(//).reduce([]) do |new_array, letter| 
    for a in 0..crazy_letters.length-1 
     if letter == crazy_letters[a] 
     new_array << letter 
     end 
    end 
    new_array 
    end 

    return true if (new_array == new_array.sort) 
end 

注:

  • return不需要在最後。不需要
  • true if ...
  • for循環永遠不應該在紅寶石
  • reduce返回塊內的最後一個表達式的結果來使用。在你的代碼中是for
  • 如果您始終需要返回reduce中的同一對象,則可能是您可以使用each_with_object的標誌。
  • "test".split只是["test"]

StringEnumerable有方法,可以幫助你。使用它們,你可以寫出更清潔和更有效的方法,如@Phrogz answer

3

問題是在致電reduce期間創建了new_array,然後之後引用丟失。 Ruby中的局部變量被限制在它們所在的塊中。在你的情況下,可以從reduce返回數組,所以你可以在那裏使用它。但是,您需要修復幾件事:

  • str.split不會將字符串分解爲Ruby +中的字符。您應該使用str.charsstr.split('')
  • 每次新迭代reduce保留的對象必須每次從塊返回時保留。最簡單的方法是將new_array作爲塊中的最後一個表達式。

這樣:

def my_useless_function(str) 
    crazy_letters = ['a','s','d','f','g','h'] 
    crazy_only = str.split('').reduce([]) do |new_array, letter| 
    for a in 0..crazy_letters.length-1 
     if letter == crazy_letters[a] 
     new_array << letter 
     end 
    end 
    new_array 
    end 

    return true if (crazy_only == crazy_only.sort) 
end 

請注意,你的功能不是很有效的,而不是非常地道。下面是更地道的功能的一個較短的版本,但沒有更有效的:

def my_useless_function(str) 
    crazy_letters = %w[a s d f g h] 
    crazy_only = str.chars.select{ |c| crazy_letters.include?(c) } 
    crazy_only == crazy_only.sort # evaluates to true or false 
end 

,這裏是一個版本,更高效:

def efficient_useless(str) 
    crazy_only = str.scan(/[asdfgh]/) # use regex to search for the letters you want 
    crazy_only == crazy_only.sort 
end 
+2

尼斯,我要寫一個更有效的版本,但它會和你的非常相似。請注意,要檢查數組是否已排序,您不需要對其進行排序(例如,您可以使用'each_cons'代替 –

+0

@Eric,也許Ruby可以使用'class Array; def排序?:enum = self.to_enum;循環{返回假如果(enum.next <=> enum.peek)== 1};真正;結束;結束; [1,2,3] .sorted? #=> true; [1,3,2] .sorted? #=> false(或者按照你的建議使用'each_cons')。 –

+2

@CarySwoveland:'Array#sorted?'將會是具有基本循環的C函數的完美候選人。 –

相關問題