【问题标题】:Why do method arguments not work for assignment?为什么方法参数不适用于赋值?
【发布时间】:2013-04-10 23:28:49
【问题描述】:

在 Ruby 中,与许多语言一样,方法的参数不会自动分配为实例变量。

这行得通:

def initialize(a)
  @a = a
end

这不是:

def initialize(@a)
end

例如,在 CoffeeScript 中,这是可行的:

constructor: (@name) ->

Ruby 中还有很多其他语法糖,例如 ||= 运算符、符号上的一元 & 等。有什么原因,技术或其他原因,为什么这个糖不是设计?

编辑

问题范围不限于initialize

在 CoffeeScript 中你也可以这样做

class Foo
  baz: (@bar) ->

【问题讨论】:

  • Ruby 没有构造函数。你能澄清你的问题吗?
  • 阅读:初始化方法
  • initialize 是一种方法,就像其他所有方法一样。它的工作原理与其他所有方法完全相同。让一种方法的行为与其他所有方法不同会非常令人困惑。
  • @JörgWMittag 编辑扩大到initialize以外的方法

标签: ruby


【解决方案1】:

不知道你在追求什么。 Ruby 2 之前的 Ruby 没有命名参数,因此要执行您所描述的操作,您需要同时传递名称和值。但在这种情况下,您不妨传递一个哈希,解析起来很简单,如下所示:https://stackoverflow.com/a/12763031/341994

【讨论】:

  • 这更多是关于Ruby的知识,我不是在这里寻找解决方法。
【解决方案2】:

在 Ruby 1.8 及更早版本中,块参数使用赋值语义而不是参数绑定语义,因此您可以这样做:

define_method(:initialize) do |@a| end

【讨论】:

  • 哇,这太酷了!据您所知,这是一个错误还是一个功能?
  • 这似乎是一个非常糟糕的主意,所以如果他们删除它,我们都会过得更好。
  • @gmalette:就像我说的,在 Ruby 1.9 之前,块参数传递是使用赋值语义完成的。 IOW:将参数传递给块的处理方式就像您将参数分配给参数一样。你甚至可以这样做:define_method(:initialize) do |foo.bar| end,每次调用块时都会调用foo.bar= 方法!这样做的原因(我认为)是您希望 some_hash.each do |key, value| end 工作,即使 Hash#each 实际上不是 yield 两个参数,而是一个参数是两个元素 Array
  • 通过并行分配,这完美地工作:key, value = [:foo, 42] 将分配:fookey42value,这就是你想要的。对于块参数,您需要相同的东西,因此将参数绑定简单地指定为赋值是有意义的。但是,有很多极端情况,例如,您不能将块分配给变量,因此您不能将块传递给块。在 1.9 中,此问题已得到修复,因此现在,块参数使用与方法相同的参数绑定语义,并添加了一些特殊的大小写来传递更多或更少的参数以及单个 Array
  • 很酷的琐事,即使它没有回答问题。
【解决方案3】:

在许多语言中这是不允许的,在 Ruby 中也是如此。例如,在 C++ 中,您不能直接在构造函数的参数列表中分配成员变量(使用初始化列表仍然需要您手动将参数复制到成员变量)。

我个人认为在构造函数主体中使用@a = a 比将initialize(@a) 直接注入到对象成员变量中更干净。

【讨论】:

  • 接口不应该进入实现细节,因此指定@a之类的东西是违反惯例的。正如您所说,某些语言直接禁止它,例如 C++。
  • @tadman,您在 1 行中表达的问题比我在整个答案中所做的更好。 +1
  • @tadman 同意,但真的有办法将 Ruby 中的接口与实现分开吗?
  • “接口”是一个概念性的东西,在 Ruby 中不存在硬边界,就像在 C 中那样,头文件通常位于单独的文件中,但这是值得尊重的。如果你开始模糊这些担忧,你就会遇到麻烦。从文档的角度来考虑,您的方法签名、参数和所有内容作为如何使用它的参考,而不是它如何做任何事情。
  • attr_reader/_writer/_accessor 的存在已经模糊了接口-实现的界限。如果我看到 attr_* :foo 已定义,则强烈建议在下面使用 @foo
【解决方案4】:

原因是 Matz 是这样决定的,至少现在是这样。有一个 open feature request 有很多支持者试图说服 Matz。

请注意,您通常应该考虑从Struct.new(:name, ...) 继承,它将为您定义一个默认构造函数来设置相应的实例变量以及访问器==eql?hash 等...

【讨论】:

  • 感谢 Struct 的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 2022-01-05
  • 1970-01-01
  • 1970-01-01
  • 2012-03-18
  • 2014-12-14
  • 1970-01-01
相关资源
最近更新 更多