Ruby:从 Class 方法中更改实例

Ruby: Change Instances from Within Class Method

我有两个要交换的 class 实例。两个实例都是数组。我想使用 class 方法交换它们。我如何 change/access class 方法 self.collide 中的实例?

class MagicBus < Array

  attr_writer :seating

  def self.collide(bus1, bus2)
    stored_arr1 = bus1
    stored_arr2 = bus2
    bus1 = stored_arr2
    bus2 = stored_arr1
    return bus1, bus2
  end

end

def test_two_magic_buses_collide_and_swap_their_passengers
    bus1 = MagicBus.new(["Mark","Dale","Peter"])
    bus1_object_id = bus1.object_id

    bus2 = MagicBus.new(["James","Patrick","Bardoe"])
    bus2_object_id = bus2.object_id

    MagicBus.collide(bus1, bus2)

    assert_equal ["James","Patrick","Bardoe"], bus1
    assert_equal bus1_object_id, bus1.object_id

    assert_equal ["Mark","Dale","Peter"], bus2
    assert_equal bus2_object_id, bus2.object_id
  end

我试过下面的代码,没有用,但应该说明我正在尝试做什么。

  def self.collide(bus1, bus2)
    stored_arr1 = bus1
    stored_arr2 = bus2
    bus1 = stored_arr2
    bus2 = stored_arr1
    self.bus1 = bus2
    self.bus2 = bus1
  end

测试结果是...

....E

Error:
TestMagicBus#test_two_magic_buses_collide_and_swap_their_passengers:
NoMethodError: undefined method `bus1=' for MagicBus:Class
    magic_bus.rb:56:in `collide'
    magic_bus.rb:126:in `test_two_magic_buses_collide_and_swap_their_passengers'

您尝试执行的操作在 Ruby 中是不可能的,因为 Ruby 没有“按引用传递”参数,但始终按指针传递。这意味着当您在方法中对参数进行赋值时,这不会更改外部变量的值:

a = "Hello"

def make_message(param)
  param = "Hello, my Friend"
end

make_message(a)

a => Still has the value "Hello"

接下来,在您的代码示例中您引用了 self.bus1,但您尚未声明这样的成员字段。这就是您收到错误的原因。

要实现您的目标,您需要使用数组 class 的方法,这会更改提供的实例:

a = [1,2,3]
b = [4,5,6]

def swap_array_content(x, y)

  temp = y.dup # Need to make a temporary copy here
  y.replace(x)
  x.replace(temp)

end

swap_array_content(a, b)

p a
p b

在 Ruby 中是 可能的。当您将一个对象传递给一个方法时,该方法接收到一个指向该对象本身的指针。这意味着在对象上调用的任何可变方法都会影响它。

您的代码不起作用,因为您只是在创建新的局部变量,而不是修改传递的对象

def self.collide(bus1, bus2)
  stored_arr1 = bus1 # assign bus1 address to stored_arr1
  stored_arr2 = bus2 # assign bus2 address to stored_arr2
  bus1 = stored_arr2 # create new local variable named bus1, assign it bus2's original address
  bus2 = stored_arr1 # create new local variable named bus2, assign it bus1's original address
  return bus1, bus2 # return bus2's original address followed by bus1's
end

您必须使用变异方法来修改传递的对象

def self.collide bus1, bus2
  tmp = bus1.dup # create a copy of bus1
  bus1.replace bus2 # replace bus1 in-place
  bus2.replace tmp # replace bus2 in-place
  # no need to return, bus1 and bus2 have been modified. the caller can use the ones passed in
end

考虑到 class 个实例完全由它们的实例变量的值来表征(前提是尚未在特定实例上定义单例方法),我们可以简单地交换这些值。

class C
  attr_reader :seating, :colour
  def initialize(seating, colour)
    @seating = seating
    @colour = colour
  end
  def self.collide(bus1, bus2)
    var2 = bus2.instance_variables.map { |v| bus2.instance_variable_get(v) }
    bus2.instance_variables.each do |v|
      bus2.instance_variable_set(v, bus1.instance_variable_get(v))
      bus1.instance_variable_set(v, var2.shift)
    end
  end
end
bus1 = C.new(46, 'blue')
bus2 = C.new(32, 'red')

[bus1.seating, bus1.colour]
  #=> [46, "blue"]
[bus2.seating, bus2.colour]
  #=> [32, "red"]

C.collide(bus1, bus2)

[bus1.seating, bus1.colour]
  #=>[32, "red"]
[bus2.seating, bus2.colour]
  #=> [46, "blue"]