【问题标题】:Unidentified method `add` for Ruby classRuby类的未知方法`add`
【发布时间】:2018-11-02 01:03:17
【问题描述】:

这是一个虚假的银行应用程序:

class Bank
    private def initialize
        $login = Hash.new
        puts "Welcome to HEIDI BANK!"
    end

    def add(keyVal)
        keyVal.each do |key, value|
            $login[key] = value
        end
    end

    def NewUser
        puts "What is the name of the user you would like to add?"
        user = gets.chomp
        puts "What is the password you would add?"
        pass = gets.chomp
        passsym = pass.to_sym
        $login.add(user => passsym)
    end
end

you_bank = Bank.new
you_bank.NewUser

当我尝试运行它时,我得到:

Welcome to HEIDI BANK!
What is the name of the user you would like to add?
12
What is the password you would add?
13

然后引发错误:

in `NewUser': undefined method `add' for {}:Hash(NoMethodError)

我该如何解决这个错误?看来我可能不得不弄清楚如何打电话给Bank.add 什么的。哈希是否具有内置的 add 函数,例如用于数组的 samp_array.push

【问题讨论】:

  • 大家都好奇,Heidi 是我妹妹的名字。这就是为什么它被称为 HEIDI 银行。
  • $login.merge(user: passsym)。见Hash documentation
  • 我在另一篇文章中读到这会删除旧的键和值。这是真的吗?
  • hash.merge(key: value) 使用hash 作为包含新键值对的模板创建一个新的Hash。如果键已经存在于hash 中,那么当merge 创建一个新的Hash 时,新的Hash 将具有新的键值对,而不是旧的。这是我在之前评论中包含的链接中的全部:“返回一个包含 other_hash 内容和 hsh 内容的新哈希
  • 或者把$login.add(user => passsym)改成self.add(user => passsym)

标签: ruby


【解决方案1】:

您将add 定义为Bank 的实例方法,但您在$login 上调用它,它是Hash 的实例。在Bank本身的实例上调用,可以省略:

def NewUser
  ...
  add(user => passsym)
end

【讨论】:

    【解决方案2】:

    我已经在 cmets 中提供了答案,但我想提供一个更长的答案来帮助你。看来您是一名年轻的程序员,并且是 Ruby 的新手,所以我想帮助您养成健康的习惯。因此,我重构了您的代码,并在 cmets 中加入了解释更改的资源链接,您可以阅读这些资源链接来帮助了解进行更改的原因。

    这些大多是次要问题,但在编写任何类型的生产代码或编写其他人将来可能必须阅读或使用的代码时它们很重要。

    # Allows the use of STDIN.noecho which will not echo text input back to the console:
    # https://stackoverflow.com/a/29334534/3784008
    require 'io/console'
    
    # Use two spaces of indentation for Ruby, not tabs and not 4 spaces:
    # https://github.com/rubocop-hq/ruby-style-guide#source-code-layout
    class Bank
      # The initialize method is not a private method:
      # https://stackoverflow.com/q/1567400/3784008
      def initialize
        # Use single quotes for non-interpolated strings
        # https://github.com/rubocop-hq/ruby-style-guide#strings
        puts 'Welcome to HEIDI BANK!'
    
        # Don't instantiate the `login` variable here; it should be lazily instantiated:
        # http://blog.jayfields.com/2007/07/ruby-lazily-initialized-attributes.html
      end
    
      # Use snake_case for method names
      # https://github.com/rubocop-hq/ruby-style-guide#naming
      def new_user
        puts 'What is the name of the user you would like to add?'
        user = gets.chomp
    
        puts 'What is the password you would add?'
        # Suppress local echo of the password as it is typed
        # https://stackoverflow.com/a/29334534/3784008
        pass = STDIN.noecho(&:gets).chomp
    
        # Do not call .to_sym on pass; if pass == an Integer then it will raise an exception,
        # e.g., 1.to_sym => NoMethodError: undefined method `to_sym' for 1:Integer
        { user => pass }
      end
    end
    

    然后像以前一样运行它:

    you_bank = Bank.new
    Welcome to HEIDI BANK!
    => #<Bank:0x00007f8cc9086710>
    you_bank.new_user
    What is the name of the user you would like to add?
    foo
    What is the password you would add?
    => {"foo"=>"bar"}
    

    关于您的原始代码的另外两个注释:

    1. 不要使用全局变量(变量名以$ 开头)。 this answer 有更多解释。如果您最终需要一个可在类实例中访问的变量,您应该使用 instance variable
    2. DRY code。不需要add(keyVal) 方法,因为它已经由mergeHash 中实现。尝试将您想要解决的问题翻译成您可以搜索的内容,在这种情况下,您想要“添加到 ruby​​ 中的哈希”,并且该查询的第一个 Google 结果是this answer,详细说明了如何这样做。

    希望对你有所帮助。

    更新

    下面你问“什么是惰性实例化?”简短的回答是:在需要使用变量之前不要分配变量。例如:

    # Bad; don't do this
    class Foo
      def initialize
        # This variable is instantiated when calling `Foo.new`, 
        # before it needs to be used, and so takes up memory right 
        # away vs. only once it's needed
        @variable_used_by_example_method = 'foobar'
      end
    
      def example_method
        puts @variable_used_by_example_method
      end
    end
    
    # Better; okay to do this
    class Foo
      def initialize
      end
    
      def example_method
        # This variable is lazily instantiated, that is, it is not 
        # instantiated until `Foo.new.example_method` is called
        @variable_used_by_example_method = 'foobar'
        puts @variable_used_by_example_method
      end
    end
    

    关于将用户名和密码与用户输入的内容进行比较的其他问题,我建议您仔细考虑问题,如果您仍然不确定,请发布一个新问题。你问的问题不够清楚,我无法给你一个好的答案。现在,您正在试验代码以弄清楚一切是如何工作的,当您试验和学习时,可以做一些不完全适合面向对象编程设计模式的事情。但是我根据你迄今为止告诉我的任何答案给你的任何答案要么与这些模式背道而驰,要么过于先进。 (例如,在 Rails 等框架中使用数据库和相关模型)

    我不会做第一个,因为那是个糟糕的建议,我不会做第二个,因为你应该首先更牢固地掌握 Ruby 和编程。所以我的建议是让你考虑这些:

    1. 您的总体目标的简单英文分步说明是什么?
    2. 如何用面向对象的逻辑来描述这种解释?
    3. 您可以编写什么代码来封装该逻辑?
    4. 您描述逻辑的能力与为该逻辑编写代码的能力之间有什么差距?

    然后你可以开始提出具体的问题来填补这些空白。

    【讨论】:

    • 什么是惰性实例化?这是一个我不熟悉的概念。我应该将其作为实例变量启动吗,例如@login?
    • 我才想起来!如何检查在 Check balance 方法中输入的用户名和密码何时与哈希中的用户/密码匹配?
    • @NathanTibbitts 我已更新我的答案以提供更多信息。
    猜你喜欢
    • 2011-05-09
    • 2020-02-03
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    相关资源
    最近更新 更多