有没有一种方法可以在Ruby中重新定义[] = +?

3

我正在尝试编写一个针对Redis的简单DSL,并且我想自己定义[]+=

我有以下代码:

def []=(key,val)
  @redis.zadd(@name,val,key)
end

我希望您能够定义

def []+=(key,val)
  @redis.zincrby(@name,val,key)
end

但是我了解到,Ruby会自动提供"[] + ="运算符,因为给定了 "[]= "

有没有一种方法来覆盖这种行为? 显然,我不希望这样做,因为我不能在流水线模式下运行它。

2个回答

6
不,Ruby中不能重新定义=。您可以尝试将返回值包装在委托到实际值的类中。这样,它们就像实际值一样运作,但是您可以使用例如“+”之类的技巧。以下是一个简单的示例:
require 'delegate'
module Redis
  class Set
    class Value < SimpleDelegator
      def +(val)
        Increment.new(self, val)
      end
    end

    class Increment < SimpleDelegator
      attr_reader :increment
      def initialize(source, increment)
        super(source.__getobj__ + increment)
        @increment = increment
      end
    end

    def [](key)
      Value.new(@redis.not_sure_what(@name, key))
    end

    def []=(key,val)
      if val.is_a?(Increment)
        @redis.zincrby(@name,val.increment,key)
      else
        @redis.zadd(@name,val,key)
      end
    end
  end
end

这只是一个起点。你需要更加谨慎,例如检查键是否相同。在我的简单示例中,redis[:foo] = redis[:bar] + 1 实际上等同于 redis[:foo] += 1...


2
+1(因为它很有趣),但我通常建议不要采用这种方法......因为增加的复杂性(和所需的知识)可能不值得。 - user166390
我同意操作符的混乱使用是不好的。它会使调试和维护变得非常困难。 - the Tin Man

2
不是的。x[y] += z 扩展成 x[y] = x[y] + z
class << (object = Object.new)
  def [](key)
    puts "[#{key.inspect}]"
    key
  end

  def []=(key, value)
    puts "[#{key.inspect}] = #{value.inspect}"
    value
  end
end

# These are all equivalent
object['See?'] += " It's impossible."
object['See?'] = object['See?'] + " It's impossible."
object.[]=('See?', object.[]('See?').+(" It's impossible."))

# They all produce the same output:
# ["See?"]
# ["See?"] = "See? It's impossible."
# => "See? It's impossible."

您需要创建一个单独的方法。

2
虽然结论是正确的,但这段代码并没有表明这是不可能的。也就是说,它并没有排除特殊的[]+=运算符语法或+=绑定规则。要“证明”这一点,只需要证明在适用的Ruby规范中没有定义这样的“特殊”运算符/绑定即可。(祝你好运找到正式的Ruby规范!:-/) - user166390
2
@pst:ISO规范已经在相当长的时间内正式批准。 - Jörg W Mittag
1
@pst,看到Ruby语言放弃参考实现而采用国际标准真是太好了。我肯定会在我的回答中引用它;这样很多C语言问题就可以优雅地解决了。不幸的是,我没有一份拷贝。 :) - Matheus Moreira
1
@pst:一般来说,RubySpec是最好的参考。所有实现都运行它,所有实现都为它做出贡献。ISO规范旨在由所有现有的Ruby实现实现,包括MRI(只能运行1.8)和YARV(只能运行1.9),因此,它仅指定了1.8和1.9的交集。事实上,它仅指定了1.8和1.9交集的一个非常小的子集,特别是标准库完全缺失,核心库缺失相当多,甚至语言也不完整。这是一个“最小有用的Ruby子集”。 - Jörg W Mittag
1
@pst,最终草案(2010年9月)已经发布。 - Matheus Moreira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接