如何在 Ruby 中使用 reduce/inject 而不获取未定义的变量

How to use reduce/inject in Ruby without getting Undefined variable

使用累加器时,累加器是只存在于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 值分配给另一个变量作为 reduce 块中的 else 语句,但这给了我相同的结果。

有人可以向我解释为什么会这样吗?

块局部变量

new_array 在您的 reduce 调用块之外不存在。这是一个 "block local variable".

reduce 做 return 一个对象,你应该在你的方法中使用它。

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 循环不应在 Ruby
  • 中使用
  • reduce returns 块内最后一个表达式的结果。在您的代码中是 for
  • 如果您总是需要 return reduce 中的同一对象,这可能是您可以使用 each_with_object.
  • 的标志
  • "test".split 就是 ["test"]

String and Enumerable have methods that could help you. Using them, you could write a much cleaner and more efficient method, as in .

问题是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