如何在Ruby中使用reduce / inject而不获取Undefined变量

时间:2017-05-05 18:05:14

标签: ruby reduce

使用累加器时,累加器是仅存在于reduce块内还是存在于函数内?

我的方法如下:

 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 个答案:

答案 0 :(得分:3)

问题是new_array是在调用reduce期间创建的,然后引用就会丢失。 Ruby中的局部变量的范围限定在它们所在的块中。在您的情况下,可以从reduce返回数组,因此您可以在那里使用它。但是,您需要解决一些问题:

  • str.split不会将字符串分解为Ruby 2+中的字符。您应该使用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

答案 1 :(得分:2)

阻止局部变量

new_arrayreduce来电的阻止之外不存在。它是"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 ...不需要
  • 永远不应该在Ruby中使用
  • for循环
  • reduce返回块内最后一个表达式的结果。您的代码中为for
  • 如果您始终需要在reduce中返回相同的对象,则可能是您可以使用each_with_object的标志。
  • "test".split只是["test"]

StringEnumerable有可以帮助您的方法。使用它们,您可以编写更清晰,更有效的方法,如@Phrogz answer