Skip to content

Instantly share code, notes, and snippets.

@ericgj
Created December 30, 2010 07:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericgj/759570 to your computer and use it in GitHub Desktop.
Save ericgj/759570 to your computer and use it in GitHub Desktop.
notes on s1-final

Demonstration of copy-on-write behavior in Ruby arrays

(No shared state between arrays)

input = [[1,2,3,4],[11,12,13,14]]
rows = input
cols = []; rows[0].each_index {|i| cols << rows.map {|r| r[i]}}
pp cols    
#  [[1, 11], [2, 12], [3, 13], [4, 14]]

#### initially the object values and references are the same

cols[0][0] == rows[0][0]
#=>  true

cols[0][0] === rows[0][0]
#=>  true

#### but after a change....

cols[0][0] = 'gotcha'

#### they differ

cols[0][0] == rows[0][0]
#=>  false

cols[0][0] === rows[0][0]
#=>  false

pp cols
#  [["gotcha", 11], [2, 12], [3, 13], [4, 14]]
pp rows
#  [[1, 2, 3, 4], [11, 12, 13, 14]]

lucasefe's solution:

Encapsulate array elements so the elements don't change but only their state

class Cell
  attr_accessor :value
  def initialize(value)
    @value = value
  end
  
  def to_s
    @value.to_s
  end
  
  def inspect
    to_s
  end
end

#### first map the values to cell instances

input = [[1,2,3,4],[11,12,13,14]]  
rows = input.map {|row| row.map {|v| Cell.new(v)}}
cols = []; rows[0].each_index {|i| cols << rows.map {|r| r[i]}}
pp cols
#  [[1, 11], [2, 12], [3, 13], [4, 14]]

cols[0][0] == rows[0][0]
#=>  true

cols[0][0] === rows[0][0]
#=>  true

#### array elements don't change, just the cell values, 
#### so references are retained across arrays

cols[0][0].value = 'new'

cols[0][0] == rows[0][0]
#=>  true

cols[0][0] === rows[0][0]
#=>  true

pp cols
#  [["new", 11], [2, 12], [3, 13], [4, 14]]
pp rows
#  [["new", 2, 3, 4], [11, 12, 13, 14]]

My solution:

  • Store all row and column elements in a single array
  • Cell objects delegate their value to the position in the array

A second encapsulation problem

If you allow direct access to the column and row arrays, and to the array elements, you break encapsulation. This is true whether you delegate to an array or provide direct access. Because then you can do the following (using lucasefe's implementation):

mytable.columns       #=> Bricks::Index object delegating to internal array
mytable.columns[0]    #=> Array of Bricks::Cells objects
mytable.columns[0] = "oops"         #=> corrupted column and rows
mytable.columns[0][0] = "mistake"   #=> corrupted cell, column and row
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment