如何在Ruby中为哈希添加值

时间:2014-04-13 00:31:20

标签: ruby methods hash

我已就此主题做了大量研究,但在我尝试的每种情况下,值都会在哈希值中被替换。在该人选择输入新ID之后,我希望将下一个人的姓名和年龄添加到哈希中。有人可以向我解释为什么要更换键和值吗?

class Person
  def initialize(name, age)
    if name != nil || age != nil
      if @people != nil
        @people[name.__id__] = age.__id__
      else
        @people = {name => age}
      end
    else
      puts "Invalid credentials."
    end
  end

  attr_reader :people
end

class MainInit
  def initialize()
    options = ["y", "yes", "n", "no"]
    choice = "y"
    while choice.downcase == "y" || choice.downcase == "yes"
      p "Enter Name:"
      inputname = gets.chomp
      p inputname

      p "Enter Age:"
      inputage = gets.chomp
      p inputage

      person = Person.new(inputname, inputage)
      p person.people

      p "Enter another ID?"
      choice = gets.chomp
      until options.include? choice.downcase
        p "Invalid Choice"
        p "Enter another ID?"
        choice = gets.chomp
      end
    end
  end
end

MainInit.new

2 个答案:

答案 0 :(得分:1)

我认为键值对被替换的原因是:

initialize方法

中的陈述
if @people != nil

将始终评估为false。创建新对象时会调用initialize,因此默认情况下@people尚未定义或设置,因此每次调用时都会

 person = Person.new(inputname, inputage)

它会创建一个新的Person,而不是将新人添加到现有的哈希(这是我认为你想要做的事情)。

如果你使people成为一个类变量(@@people),可能有效,但似乎你只想在主程序中创建一个Hash然后再添加那里的新条目。

这样的事情

people = Hash.new # Or even just people = {}

然后当你有一个新的名字/年龄条目要添加

people[name] = age

我没有尝试过,但我认为你的整个程序应该简化为:

people = Hash.new
options = ["y", "yes", "n", "no"]
    choice = "y"
    while choice.downcase == "y" || choice.downcase == "yes"
      p "Enter Name:"
      inputname = gets.chomp
      p inputname

      p "Enter Age:"
      inputage = gets.chomp
      p inputage

      #person = Person.new(inputname, inputage)
      people[inputname] = inputage
      person = people[inputname]
      p person.people

      p "Enter another ID?"
      choice = gets.chomp
      until options.include? choice.downcase
        p "Invalid Choice"
        p "Enter another ID?"
        choice = gets.chomp
      end

答案 1 :(得分:0)

让我们解释为什么你遇到了你描述的问题,并提供了一些关于如何更改代码的建议。

<强> class Person

在类Person中,您需要在类级别保存人员列表,这意味着使用类实例变量(例如@people)或类变量(例如, @@people)。我和大多数Rubie在偏爱前者。 (原因超出了这个答案的范围,但你会发现很多关于这个主题的写作,只需谷歌搜索,“Ruby'类实例变量'与'类变量'”。内部引号 - 你输入的唯一 - - 帮助缩小搜索范围。)

要定义类实例变量@people,我们只需输入如下:

class Person
  @people = {}
  class << self
    attr_accessor :people
  end
  def initialize(name, age)
    self.class.people[name] = age
  end
end

@表示它是一个实例变量。一旦Ruby读取class Person,它就会将self设置为Person。然后它读取@people = {}并使其成为Person的实例变量。相比之下,如果您要在@person方法中初始化initialize,那么self将是Person的实例,因此@person将是一个普通的实例变量。 (旁白:我们可以同时拥有一个类实例变量@person和一个实例变量@person,Ruby会将它们视为与@night@day不同。)< / p>

为了让对象访问@people,我们定义了一个访问者。如果我们刚刚输入attr_accessor :person,Ruby将为常规实例变量@person创建一个访问器。相反,我们输入class << self,它指示Ruby将与后面的end之后的内容相关联。

每次创建一个新的Person实例时,对于给定的nameage

self.class.people[name] = age

向哈希@person添加一个元素,因为self.classPersonpeople是访问者。

现在看一下班级MainInit

班级MainInit

class MainInit
  def initialize
    loop do
      name = nil
      loop do
        print 'Enter Name: '
        name = gets.strip
        break unless name.empty?
      end  
      puts "name is #{name}"
      age = nil
      loop do
        print 'Enter Age: '
        age = gets.strip
        case age
        when /^\d+$/ && ('10'..'120')
          break
        else
          puts 'age must be between 10 and 120'
        end  
      end  
      puts "age is #{age}"
      person = Person.new(name, age)
      puts "people is now #{Person.people}"
      loop do
        print "Enter another ID? "
        case gets.chomp.downcase
        when 'n', 'no'
          return
        when 'y', 'yes'
          break
        else
          puts 'Invalid choice'
        end  
      end
    end
  end  
end

loop do...end

您看到我在几个地方使用loop do...endbreak退出循环。与循环while...或或until...相比,我是这个结构的忠实粉丝,部分原因是它避免了进入循环进入循环然后重复相同条件的需要循环。我也觉得它看起来更干净。

当您离开循环时,循环内创建的任何变量都不再存在,因此如果您想要变量的值(例如,nameage),则必须在开头之外引用变量的循环。这就是为什么(也是唯一的原因)我有name = nilage = nil。它不一定是nil;我可以将它们初始化为任何东西。

使用case声明

获得年龄的循环使用此案例陈述:

case age
when /^\d+$/ && ('10'..'120')
  ...
end  

这需要一些解释。 case语句使用String#===而不是String#==来获取真值。因此when /^\d+$/相当于:

/^\d+$/ === age

相同
/^\d+$/ =~ age

正则表达式只是确保所有年龄字符都是数字(例如“39”)。

类似地,

('10'..'120') === age

相同
('10'..'120').cover?(age)

赔率和结束

我使用String#strip代替String#chomp。两者都删除了结尾换行符,但strip也删除了用户可能在输入字符串的开头或结尾输入的空格。

对于字符串,我主要使用单引号,但字符串插值需要双引号。例如,我最初写了puts 'name is #{name}'。打印name is #{name}。将其更改为puts "name is #{name}"后,它正确打印name is Debra

示例

MainInit.new
Enter Name: Debra       
name is Debra
Enter Age: 29
age is 29
people is now {"Debra"=>"29"}
Enter another ID? y
Enter Name: Billy-Bob
name is Billy-Bob
Enter Age: 58
age is 58
people is now {"Debra"=>"29", "Billy-Bob"=>"58"}
Enter another ID? u
Invalid choice
Enter another ID? n