为什么初始化变量如此重要?

Joa*_*a B 6 ruby variables ruby-on-rails

请有人向我解释,为什么不初始化first_idxlast_idx会导致代码无法运行?

当我运行它时,我得到这个错误"未定义的局部变量或方法last_idx".我知道建议是始终初始化变量,但我不明白为什么.毕竟first_idx和last_idx总是会在循环中得到一个值,因为参数lette r总是出现在字符串中(在这个特殊的问题中).

我真的很感激一些(简单的)洞察力.谢谢!

PS,我也知道在Ruby中使用#index和#rindex很容易解决这个问题,但我不允许用简单的方法解决它.

def find_for_letter(string, letter)

first_idx = nil
0.upto(string.length - 1) do |idx1|
    if string[idx1] == letter
        first_idx = idx1
        break
    end
end

last_idx = nil
(string.length - 1).downto(0) do |idx2|
    if string[idx2] == letter
        last_idx = idx2
        break
    end
end

if last_idx == first_idx
    return [first_idx]
else
    return [first_idx, last_idx]
end
end



def first_last_indices(word)
    h = {}
    word.chars.each do |char|
        h[char] = find_for_letter(word, char)
    end
    h
end
Run Code Online (Sandbox Code Playgroud)

Eri*_*nil 6

块中的变量

Ruby编程语言:

块定义了一个新的变量范围:块内创建的变量仅存在于该块内,并且在块外部未定义.但是要小心; 方法中的局部变量可用于该方法中的任何块.因此,如果块为已经在块外部定义的变量赋值,则不会创建新的块局部变量,而是为已存在的变量分配新值.

a = 0

2.times do
  a = 1
end

puts a #=> 1

b = 0

2.times do |i;b| # <- b will stay a block-local variable 
  b = 1
end

puts b #=> 0

2.times do |i|
  c = 1
end

puts c #=> undefined local variable or method `c' for main:Object (NameError)
Run Code Online (Sandbox Code Playgroud)

重构您的代码

用字符和索引迭代

这是一个较小的方法来实现目标.它为每个字符保留一个带有minmax索引的哈希值.

默认哈希值是一个空数组.

该方法遍历每个字符(带索引).

如果minmax数组已包含2个值:

  • 它用当前索引替换第二个(最大).
  • 它会向数组添加当前索引.


def first_last_indices(word)
  minmax_hash = Hash.new { |h, k| h[k] = [] }
  word.each_char.with_index do |char, index|
    minmax = minmax_hash[char]
    if minmax.size == 2
      minmax[1] = index
    else
      minmax << index
    end
  end
  minmax_hash
end

p first_last_indices('hello world')
{"h"=>[0], "e"=>[1], "l"=>[2, 9], "o"=>[4, 7], " "=>[5], "w"=>[6], "r"=>[8], "d"=>[10]}
Run Code Online (Sandbox Code Playgroud)

使用group_by

这是另一种可能性.它使用group_by获取每个字符的所有索引,而minmax只获取第一个和最后一个索引:

def first_last_indices(word)
  word.each_char.with_index
      .group_by{ |c, _| c }.map{ |c, vs|
        [c, vs.map(&:last).minmax.uniq]
      }.to_h
end

p first_last_indices('hello world')
{"h"=>[0], "e"=>[1], "l"=>[2, 9], "o"=>[4, 7], " "=>[5], "w"=>[6], "r"=>[8], "d"=>[10]}
Run Code Online (Sandbox Code Playgroud)


shi*_*vam 5

即使你没有声明last_idx,你仍然可以在循环内初始化它,即:

(string.length - 1).downto(0) do |idx2|
    if string[idx2] == letter
        last_idx = idx2 # works absolutely fine
        break
    end
end
Run Code Online (Sandbox Code Playgroud)

但请注意您声明变量的位置.它是一个局部变量,因此它与您所在的块相关联.现在,当您尝试在块外部访问该变量时,您会收到错误:

undefined局部变量或方法last_idx

要使变量在块外可用,您必须在外部声明它.当你last_idx = nil在为其分配值的块之前声明时,这就是你正在做的事情.

更新:

虽然通过使用实例变量可以避免声明,但最佳实践表明它应该用于这些变量所具有的信息与所有或几乎所有类相关的情况.另一方面,如果信息非常局限于这种特定方法,则使用局部变量.