0

I am trying to write a Greedy Algorithm for a certain problem. Simplified it looks like this:

There's an object called Foo with an randomized attribute called value and a method that changes this value change_value in a way that depends on an integer input

class Foo
  def initialize
    value = rand(1,10)
  end

  def change_value(input)
    #changes the value in a certain way
  end
end

Now the Greedy Algorithmus just gets the new value of Foo for all possible inputs and return the best input.

foo = Foo.new

best_value = 0
best_input = 0

(1..inputs).each do |k|
  temp_foo = foo.clone
  temp_foo.change_value(k)
  if temp_foo.value>best_value
    best_value = temp_foo.value
    best_input = k
  end
end

Foo.change_value(best_input)

The code works nearly as intended. The big problem is that the change_value-method within the each-funtion alters the temp_foo and the foo. What do I need to change to makes those objects completly dependent of each other? I also tried .dub by the way.

Syk
  • 393
  • 4
  • 19

2 Answers2

1

I think #clone or #dup won't work because they will share a reference to @value inside Foo.

In any case, you can do it more readably by changing Foo#change_value so it doesn't actually mutate the object but returns a copy:

class Foo
  def initialize(value = nil)
    @value = value || rand(10)
  end

  def change_value(input)
    # returns a new Foo instance
    Foo.new(@value + 1)
  end

  def value
    @value
  end
end

Because you're copying data in any case, using an immutable object (Value Object) is more general than some kind of deep clone.

Marcin Bilski
  • 572
  • 6
  • 13
  • Did it mostly this way in the end. Just added an optional parameter to the initalizer so that I can call on `foo` this: `foo_copy = Foo.new(foo.value)` – Syk Nov 19 '15 at 12:16
0

I assume you assign value to the instance variable @value in Foo#initialize not the local variable value.

I also assume you don't have a simple primitive like in your code above but rather another object that contains a pointer, otherwise you most probably would not have such problem. In other words, I assume your change_value method makes an operation that relies on the @value pointer, such as @value[key] = some_new_value and not pure assignment, such as @value = some_new_object. When your object gets copied with clone or dup, that particular pointer is being copied, instead of the underlying structure, and therefore any calls to temp_foo.change_value will result in changes to foo's underlying @value.

To avoid this, you need to duplicate the object @value refers to. There is a trick you can use with Marshal, as discussed in this post, but I recommend against it since it causes a great deal of overhead. Instead, I would define a deep_dup method, such as below:

class Foo
  def deep_dup
    # Either this
    @value = @value.dup

    # OR this, and define the method #deep_dup in the class of @value 
    # to dup its internal structure too:
    @value = @value.deep_dup
  end
end

Then instead of doing temp_foo = foo.clone do temp_foo = foo.deep_dup.

Community
  • 1
  • 1
AmitA
  • 3,239
  • 1
  • 22
  • 31