首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Ruby:自动将实例变量设置为方法参数?

Ruby:自动将实例变量设置为方法参数?
EN

Stack Overflow用户
提问于 2012-06-02 03:23:11
回答 3查看 7.2K关注 0票数 10

是否有计划实现类似于在方法参数列表中指定实例变量名的CoffeeScript特性的ruby行为?喜欢

代码语言:javascript
复制
class User
  def initialize(@name, age)
    # @name is set implicitly, but @age isn't.
    # the local variable "age" will be set, just like it currently works.
  end
end

我知道这个问题:in Ruby can I automatically populate instance variables somehow in the initialize method?,但是所有的解决方案(包括我自己的)似乎都不符合ruby简单性的哲学。

而且,这种行为会有什么负面影响吗?

更新

原因之一是ruby社区的枯燥(不要重复自己)哲学。我经常发现自己需要重复参数变量的名称,因为我希望将其赋给同名的实例变量。

代码语言:javascript
复制
def initialize(name)
  # not DRY
  @name = name
end

我能想到的一个缺点是,如果一个方法没有主体,它可能看起来什么都不做。如果你扫描的速度很快,这可能看起来像是一个no-op。但我认为,如果有时间,我们可以适应。

另一个缺点是:如果在主体中设置其他实例变量,并试图通过将所有赋值放在开头来使其具有可读性,则可能需要更多的认知“能力”才能看到参数列表中也发生了赋值。但我不认为这比看到一个常量或方法调用并必须跳转到它的定义更难。

代码语言:javascript
复制
# notice: instance var assignments are happening in 2 places! 
def initialize(@name)
  @errors = []
end
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-06-03 06:13:08

经过深思熟虑后,我想知道是否有可能真正从ruby方法中获得参数名称。如果是这样,我可以使用像"iv_“这样的特殊参数前缀来指示应该将哪些参数设置为实例变量。

这是可能的:How to get argument names using reflection

是!所以我可以写一个模块来帮我处理这个问题。然后我被卡住了,因为如果我调用模块的helper方法,它不知道参数的值,因为它们对于调用者来说是本地的。啊,但是ruby有绑定对象。

以下是模块(仅限ruby 1.9 ):

代码语言:javascript
复制
module InstanceVarsFromArgsSlurper
  # arg_prefix must be a valid local variable name, and I strongly suggest
  # ending it with an underscore for readability of the slurped args.
  def self.enable_for(mod, arg_prefix)
    raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i
    mod.send(:include, self)
    mod.instance_variable_set(:@instance_vars_from_args_slurper_prefix, arg_prefix.to_s)
  end

  def slurp_args(binding)
    defined_prefix = self.class.instance_variable_get(:@instance_vars_from_args_slurper_prefix)
    method_name = caller[0][/`.*?'/][1..-2]
    param_names = method(method_name).parameters.map{|p| p.last.to_s }
    param_names.each do |pname|
      # starts with and longer than prefix
      if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1
        ivar_name = pname[defined_prefix.size .. -1]
        eval "@#{ivar_name} = #{pname}", binding
      end
    end
    nil
  end
end

下面是用法:

代码语言:javascript
复制
class User
  InstanceVarsFromArgsSlurper.enable_for(self, 'iv_')

  def initialize(iv_name, age)
    slurp_args(binding)  # this line does all the heavy lifting
    p [:iv_name, iv_name]
    p [:age, age]
    p [:@name, @name]
    p [:@age, @age]
  end
end

user = User.new("Methuselah", 969)
p user

输出:

代码语言:javascript
复制
[:iv_name, "Methuselah"]
[:age, 969]
[:@name, "Methuselah"]
[:@age, nil]
#<User:0x00000101089448 @name="Methuselah">

它不会让你有一个空的方法体,但它是干的。我确信,只需指定哪些方法应该具有此行为(通过alias_method实现),而不是在每个方法中调用slurp_args,它就可以得到进一步的增强-尽管该规范必须在定义完所有方法之后才能完成。

请注意,模块和帮助器方法名称可能会有所改进。我只用了我想到的第一件事。

票数 4
EN

Stack Overflow用户

发布于 2012-06-02 10:36:40

实际上..。

代码语言:javascript
复制
class User
  define_method(:initialize) { |@name| }
end

User.new(:name).instance_variable_get :@name
# => :name

适用于1.8.7,但不适用于1.9.3。现在,我从哪里学到这个的..。

票数 2
EN

Stack Overflow用户

发布于 2012-06-02 04:32:16

我认为你回答了你自己的问题,它不符合ruby的简单性哲学。这将增加如何在方法中处理参数的复杂性,并将管理变量的逻辑上移到方法参数中。我可以看到这样做会降低代码的可读性的争论,但它确实让我觉得不是很冗长。

@ param将不得不处理一些场景:

代码语言:javascript
复制
def initialize( first, last, @scope, @opts = {} )

def search( @query, condition )

def ratchet( @*arg  ) 

所有这些场景都应该有效吗?只有initialize吗?在我看来,@*arg似乎特别危险。所有这些规则和排除使得Ruby语言更加复杂。为了自动实例变量的好处,我不认为这是值得的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10856191

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档