【问题标题】:How to Add Values to a Hash in Ruby如何在 Ruby 中向哈希值添加值
【发布时间】:2014-04-13 00:31:19
【问题描述】:

我对这个主题做了很多研究,但在我尝试的每一种情况下,哈希值似乎都被替换了。在此人选择输入新 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

【问题讨论】:

  • @people 是一个实例变量,因此每个实例都有一个。每个只包含一个键/值对。我猜您想将与所有实例变量关联的名称和年龄存储在@people 中。如果是这样,您需要将@people 设为类实例变量(或使用类变量,如@@people)。
  • 你需要重组你的类,因为你想保持状态,你需要将attr_reader :people更改为attr_accessor :people,这样你就可以改变人员哈希的结构,我也不认为你的使用__id__是正确或必要的,将MainInit的initialize方法中的所有逻辑移动到另一个方法中,只留下@people = Person.new(nil, nil),然后你可以调用你创建的新方法例如MainInit.new.new_method而不是MainInit.new 在脚本末尾
  • 我很快就会给你另一个答案。

标签: ruby methods hash


【解决方案1】:

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

initialize 方法中的语句

if @people != nil

将始终评估为falseinitialize是在创建新对象的时候调用的,所以默认@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

【讨论】:

    【解决方案2】:

    让我解释一下你为什么会遇到你所描述的问题,并就如何更改代码提供一些建议。

    class Person

    Person 类中,您需要在类级别保存人员列表,这意味着使用类实例变量(例如,@people)或类变量(例如,@@people) .我和大多数红宝石一样偏爱前者。 (原因超出了这个答案的范围,但你会通过简单的谷歌搜索找到很多关于这个主题的文章,“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 的实例变量。相比之下,如果您要在 initialize 方法中初始化 @person,那么此时 self 将是 Person 的一个实例,因此 @person 将是一个普通的实例变量。 (顺便说一句:我们可以同时拥有一个类实例变量@person 和一个实例变量@person,Ruby 会像对待@night@day 一样对待它们。)

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

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

    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语句:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-09
      • 2013-12-17
      • 2016-02-10
      • 1970-01-01
      • 2013-03-02
      • 2020-12-09
      • 1970-01-01
      相关资源
      最近更新 更多