class FlexArray

A flexible array class.

Append method support for the flex array.

Support for each and related methods for the flex array.

Forever looping support for the flexible array class.

The flexible array indexing and related methods.

The flexible array class constructor and related methods.

The flexible array class index processing routines.

The flexible array class reshape and related methods.

The flexible array class transpose and related methods.

Some flexible array class index validation routines.

Constants

FOREVER

Create a forever constant for use where infinite looping is needed.

VERSION

The version string for flex array.

Attributes

array_data[RW]

The underlying array data used by the flex array.

array_specs[RW]

The array specifications. An array of spec components.

transposed[R]

Is this flex array transposed?

Public Class Methods

new(array_specs, default=nil, &init_block) click to toggle source

Construct a flexible array object.

# File lib/flex_array/flex_array_new.rb, line 4
def initialize(array_specs, default=nil, &init_block)
  @array_specs = SpecArray.new(array_specs.in_array)
  @transposed = false

  # Allocate the data for the array.
  @array_data = Array.new(@array_specs.spec_count, default)

  # Set up the array with the optional init_block.
  if init_block
    process_all {|index, posn| @array_data[posn] = init_block.call(index)}
  end
end
new_from(array_specs, other) click to toggle source

Construct a flex array using other as a template or data source.

# File lib/flex_array/flex_array_new.rb, line 28
def self.new_from(array_specs, other)
  iterator = other.array_data.cycle
  FlexArray.new(array_specs) {iterator.next}
end
new_from_array(other) click to toggle source

Construct a flex array as a duplicate of a source array or flex array.

# File lib/flex_array/flex_array_new.rb, line 41
def self.new_from_array(other)
  result = FlexArray.new(0)
  result.array_specs = other.array_specs.dup
  result.array_data = other.array_data
  result
end
new_from_selection(array_specs, other, selection) click to toggle source

Construct a flex array using a all or a portion of portion of another flex array as a data source.

# File lib/flex_array/flex_array_new.rb, line 35
def self.new_from_selection(array_specs, other, selection)
  iterator = other.select_cycle(selection)
  FlexArray.new(array_specs) {iterator.next}
end
version() click to toggle source

The version of this class. “<major>.<minor>.<step>”

# File lib/flex_array.rb, line 27
def self.version
  FlexArray::VERSION
end

Public Instance Methods

<<(data) click to toggle source

Append to the flex array.

# File lib/flex_array/flex_array_append.rb, line 5
def << (data)
  fail "Cannot append to a transposed array." if @transposed
  specs = get_append_specs(data = data.in_array)
  @array_data += data.array_data
  @array_specs.enlarge(specs[0].span)
  self
end
<=>(other) click to toggle source

Make FlexArrays comparable with the compariositality method.

# File lib/flex_array.rb, line 73
def <=>(other)
  @array_data <=> other.array_data
end
==(other) click to toggle source

Are these FlexArrays equal?

# File lib/flex_array.rb, line 68
def ==(other)
  self.compatible?(other) && @array_data == other.array_data
end
[](*indexes) click to toggle source

Retrieve the selected data from the flex array.

# File lib/flex_array/flex_array_index.rb, line 5
def [](*indexes)
  validate_index_count(indexes)
  result = []
  process_indexes(indexes) {|_index, posn| result << @array_data[posn]}
  result.length == 1 ? result[0] : result
end
[]=(*indexes, value) click to toggle source

Store the value data into the flex array.

# File lib/flex_array/flex_array_index.rb, line 13
def []=(*indexes, value)
  validate_index_count(indexes)
  source = value.in_array.cycle
  process_indexes(indexes) {|_index, posn| @array_data[posn] = source.next}
  value
end
_each_raw(&block) click to toggle source

A specialized each variant that passes the low level data, the index and the position to the block.

# File lib/flex_array/flex_array_each.rb, line 95
def _each_raw(&block)
  if block_given?
    process_all {|index, posn| block.call(index, posn)}
    self
  else
    self.to_enum(:_each_raw)
  end
end
_select_each_raw(indexes, &block) click to toggle source

An enhanced specialized each variant that passes the low level data, the index and the position to the block.

# File lib/flex_array/flex_array_each.rb, line 106
def _select_each_raw(indexes, &block)
  validate_index_count(indexes)

  if block_given?
    process_indexes(indexes) {|index, posn| block.call(index, posn)}
    self
  else
    self.to_enum(:_select_each_raw, indexes)
  end
end
collect(&block) click to toggle source

The flex array version of collect that returns a flex array.

# File lib/flex_array/flex_array_each.rb, line 120
def collect(&block)
  result = self.dup
  result.collect!(&block)
end
Also aliased as: flatten_collect
collect!(&block) click to toggle source

The flex array version of collect!

# File lib/flex_array/flex_array_each.rb, line 147
def collect!(&block)
  fail ArgumentError, "A block is required." unless block_given?

  if @transposed
    process_all {|_index, posn| @array_data[posn] =
      block.call(@array_data[posn])}
  else
    @array_data = @array_data.collect(&block)
  end

  self
end
compatible?(other) click to toggle source

Is this array compatible with other?

# File lib/flex_array/flex_array_validate.rb, line 5
def compatible?(other)
  @array_specs == other.array_specs
rescue
  false
end
cycle(count = FOREVER, &block) click to toggle source

Retrieve data from the array endlessly repeating as needed.

# File lib/flex_array/flex_array_each.rb, line 7
def cycle(count = FOREVER, &block)
  if block_given?
    if @transposed && length > 0
      count.times do
        process_all do |_index, posn|
          block.call(@array_data[posn])
        end
      end

      nil
    else
      @array_data.cycle(count.to_i, &block)
    end
  else
    self.to_enum(:cycle, count)
  end
end
dimensions() click to toggle source

The number of dimensions in this array.

# File lib/flex_array.rb, line 50
def dimensions
  @array_specs.spec_dimensions
end
dup() click to toggle source

Create a duplicate of this array.

# File lib/flex_array/flex_array_new.rb, line 20
def dup
  other = self.shallow_dup
  other.array_specs = @array_specs.dup
  other.array_data  = @array_data.dup
  other
end
Also aliased as: shallow_dup
each(&block) click to toggle source

Process the standard each operator.

# File lib/flex_array/flex_array_each.rb, line 45
def each(&block)
  if block_given?
    if @transposed
      process_all {|_index, posn| block.call(@array_data[posn])}
    else
      @array_data.each(&block)
    end

    self
  else
    self.to_enum(:each)
  end
end
each_with_index(&block) click to toggle source

Process the standard each_with_index operator.

# File lib/flex_array/flex_array_each.rb, line 72
def each_with_index(&block)
  if block_given?
    process_all {|index, posn| block.call(@array_data[posn], index)}
    self
  else
    self.to_enum(:each_with_index)
  end
end
empty?() click to toggle source

Is this flex array empty?

# File lib/flex_array.rb, line 78
def empty?
  length == 0
end
find_index(value = nil, &block) click to toggle source

The flex array version of find_index. This returns the coordinates of the first object that matches the search object or is flagged true by the search block.

# File lib/flex_array/flex_array_each.rb, line 175
def find_index(value = nil, &block)
  blk = get_find_block(value, &block)

  if blk
    process_all do |index, posn|
      if blk.call(@array_data[posn])
        return index
      end
    end

    nil
  else
    self.to_enum(:find_index)
  end
end
find_indexes(value = nil, &block) click to toggle source

The improved flex array version of find_index. This returns the coordinates of objects that match the search object or are flagged true by the search block.

# File lib/flex_array/flex_array_each.rb, line 214
def find_indexes(value = nil, &block)
  blk, result = get_find_block(value, &block), []

  if blk
    process_all do |index, posn|
      if blk.call(@array_data[posn])
        result << index.dup
      end
    end

    result
  else
    self.to_enum(:find_indexes)
  end
end
flatten_collect(&block)
Alias for: collect
length() click to toggle source

The total number of elements in this array.

# File lib/flex_array.rb, line 43
def length
  @array_specs.spec_count
end
Also aliased as: size
limits() click to toggle source

Get the limits of the subscripts of the flex array.

# File lib/flex_array.rb, line 58
def limits
  @array_specs.collect {|spec| spec.range }
end
reshape(array_specs) click to toggle source

Return a copy of this flex array, recast in a new shape, dropping or repeating data elements as required.

# File lib/flex_array/flex_array_reshape.rb, line 6
def reshape(array_specs)
  iterator = @array_data.cycle
  FlexArray.new(array_specs) {iterator.next}
end
reshape!(array_specs) click to toggle source

Recast this flex array in a new shape, dropping or repeating data elements as required.

# File lib/flex_array/flex_array_reshape.rb, line 13
def reshape!(array_specs)
  temp = self.reshape(array_specs)
  @array_specs, @array_data = temp.array_specs, temp.array_data
  self
end
select_collect(indexes, &block) click to toggle source

The flex array version of collect that accepts an optional set of indexes to select the data being collected into a flex array.

# File lib/flex_array/flex_array_each.rb, line 127
def select_collect(indexes, &block)
  result = self.dup
  result.select_collect!(indexes, &block)
end
select_collect!(indexes, &block) click to toggle source

The enhanced flex array version of collect! that accepts a set of indexes to select the data being collected.

# File lib/flex_array/flex_array_each.rb, line 162
def select_collect!(indexes, &block)
  fail ArgumentError, "A block is required." unless block_given?
  validate_index_count(indexes)

  process_indexes(indexes) {|_index, posn| @array_data[posn] =
    block.call(@array_data[posn])}

  self
end
select_cycle(indexes, count = FOREVER, &block) click to toggle source

Retrieve data from a subset of the flex array endlessly repeating as needed.

# File lib/flex_array/flex_array_each.rb, line 26
def select_cycle(indexes, count = FOREVER, &block)
  validate_index_count(indexes)

  if block_given?
    unless empty?
      count.times do
        process_indexes(indexes) do |_index, posn|
          block.call(@array_data[posn])
        end
      end
    end

    nil
  else
    self.to_enum(:select_cycle, indexes, count)
  end
end
select_each(indexes, &block) click to toggle source

Process the enhanced select_each operator.

# File lib/flex_array/flex_array_each.rb, line 60
def select_each(indexes, &block)
  validate_index_count(indexes)

  if block_given?
    process_indexes(indexes) {|_index, posn| block.call(@array_data[posn])}
    self
  else
    self.to_enum(:select_each, indexes)
  end
end
select_each_with_index(indexes, &block) click to toggle source

Process the enhanced select_each_with_index operator.

# File lib/flex_array/flex_array_each.rb, line 82
def select_each_with_index(indexes, &block)
  validate_index_count(indexes)

  if block_given?
    process_indexes(indexes) {|index, posn| block.call(@array_data[posn], index)}
    self
  else
    self.to_enum(:select_each_with_index, indexes)
  end
end
select_find_index(indexes, value = nil, &block) click to toggle source

The enhanced flex array version of find_index. This returns the coordinates of the first object that matches the search object or is flagged true by the search block.

# File lib/flex_array/flex_array_each.rb, line 194
def select_find_index(indexes, value = nil, &block)
  validate_index_count(indexes)
  blk = get_find_block(value, &block)

  if blk
    process_indexes(indexes) do |index, posn|
      if blk.call(@array_data[posn])
        return index
      end
    end

    nil
  else
    self.to_enum(:select_find_index, indexes)
  end
end
select_find_indexes(indexes, value = nil, &block) click to toggle source

The enhanced and improved flex array version of find_index. This returns the coordinates of objects that match the search object or are flagged true by the search block.

# File lib/flex_array/flex_array_each.rb, line 233
def select_find_indexes(indexes, value = nil, &block)
  validate_index_count(indexes)
  blk, result = get_find_block(value, &block), []

  if blk
    process_indexes(indexes) do |index, posn|
      if blk.call(@array_data[posn])
        result << index.dup
      end
    end

    result
  else
    self.to_enum(:select_find_index, indexes)
  end
end
select_flatten_collect(indexes, &block) click to toggle source

The flex array version of collect that accepts an optional set of indexes to select the data being collected into a standard array.

# File lib/flex_array/flex_array_each.rb, line 134
def select_flatten_collect(indexes, &block)
  validate_index_count(indexes)

  if block_given?
    result = []
    process_indexes(indexes) {|_index, posn| result << block.call(@array_data[posn])}
    result
  else
    self.to_enum(:select_collect, indexes)
  end
end
shallow_dup()
Alias for: dup
size()
Alias for: length
to_a() click to toggle source

Convert the flex array to a simple array. Contained arrays are not affected.

# File lib/flex_array/flex_array_reshape.rb, line 20
def to_a
  if @transposed
    fetch = self.cycle
    Array.new(@array_data.length) { fetch.next }
  else
    @array_data.dup
  end
end
to_flex_array() click to toggle source

Return this flex array as a flex array!

# File lib/flex_array.rb, line 63
def to_flex_array
  self
end
transpose(dim_a, dim_b) click to toggle source

Return a reference to this array's data with the specified dimensions transposed. This may change the “shape” of the array if the transposed dimensions were of different limits.

# File lib/flex_array/flex_array_transpose.rb, line 17
def transpose(dim_a, dim_b)
  FlexArray.new_from_array(self).transpose!(dim_a, dim_b)
end
transpose!(dim_a, dim_b) click to toggle source

Transpose the specified dimensions. This may change the “shape” of the array if the transposed dimensions were of different limits.

# File lib/flex_array/flex_array_transpose.rb, line 6
def transpose!(dim_a, dim_b)
  validate_dimension(dim_a)
  validate_dimension(dim_b)
  @array_specs[dim_a], @array_specs[dim_b] = @array_specs[dim_b], @array_specs[dim_a]
  @transposed = @array_specs.transposed?
  self
end
version() click to toggle source

The version of the class of this instance.

# File lib/flex_array.rb, line 32
def version
  FlexArray::VERSION
end

Private Instance Methods

get_append_specs(data) click to toggle source

Extract and validate the append array_spec

# File lib/flex_array/flex_array_append.rb, line 15
def get_append_specs(data)
  spec_len = (specs = data.array_specs).length

  if dimensions == spec_len+1
    specs = specs.dup.insert(0, SpecComponent.new(0...1, nil))
  elsif dimensions != spec_len
    fail ArgumentError, "Incompatible dimensionality error on <<."
  end

  (1...dimensions).each do |index|
    unless @array_specs[index].span == specs[index].span
      fail ArgumentError, "Dimension mismatch error on <<."
    end
  end

  specs
end
get_find_block(value, &block) click to toggle source

A helper method to determine which block to use in the find_index family.

# File lib/flex_array/flex_array_each.rb, line 253
def get_find_block(value, &block)
  if block_given?
    block
  else
    lambda {|obj| obj == value }
  end
end
process_all(&block) click to toggle source

Special case where all of the array is being processed.

# File lib/flex_array/flex_array_process.rb, line 46
def process_all(&block)
  current = Array.new(dimensions, 0)
  process_all_worker(0, 0, current, &block)
end
process_all_worker(depth, posn, current, &block) click to toggle source

The worker bee for process_all.

# File lib/flex_array/flex_array_process.rb, line 52
def process_all_worker(depth, posn, current, &block)
  if depth == dimensions                 # Is there more work to do?
    block.call(current, posn)            # Index ready, call the block.
  else
    spec = @array_specs[depth]           # Get the current specification.
    stride = spec.stride

    spec.each do |index|                 # Iterate over the range.
      current[depth] = index             # Update the current index.

      # Process the next component in the array specification.
      process_all_worker(depth+1, posn, current, &block)
      posn += stride                     # Step to the next position.
    end
  end
end
process_indexes(indexes, &block) click to toggle source

Process a flex array index array. This is the heart of the flex array indexing process.

# File lib/flex_array/flex_array_process.rb, line 7
def process_indexes(indexes, &block)
  current = Array.new(dimensions, 0)

  if indexes == [:all]
    process_all_worker(0, 0, current, &block)
  else
    specs = @array_specs.each

    checked = indexes.collect do |index|
      index.to_index_range(specs.next)
    end

    process_indexes_worker(0, 0, checked, current, &block)
  end
end
process_indexes_worker(depth, posn, indexes, current, &block) click to toggle source

The worker bee for process_indexes. :reek: LongParameterList

# File lib/flex_array/flex_array_process.rb, line 25
def process_indexes_worker(depth, posn, indexes, current, &block)
  if depth == dimensions                 # Is there more work to do?
    block.call(current, posn)            # Index ready, call the block.
  else
    spec = @array_specs[depth]           # Get the current specification.
    min, stride = spec.min, spec.stride  # Extract the relevant info.

    indexes[depth].each do |index|       # Iterate over the range.
      current[depth] = index             # Update the current index.

      # Process the next component in the array index.
      process_indexes_worker(depth+1,
                             posn + (index-min) * stride,
                             indexes,
                             current,
                             &block)
    end
  end
end
validate_dimension(dim) click to toggle source

Is this a valid dimension selector?

# File lib/flex_array/flex_array_validate.rb, line 23
def validate_dimension(dim)
  unless (0...dimensions) === dim
    fail ArgumentError, "Invalid dimension selector: #{dim}"
  end
end
validate_index_count(indexes) click to toggle source

Validate the dimensionality of the indexes passed in.

# File lib/flex_array/flex_array_validate.rb, line 14
def validate_index_count(indexes)
  unless indexes == [:all]
    if dimensions != indexes.length
      fail ArgumentError, "Incorrect number of indexes: #{dimensions} expected."
    end
  end
end