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
The underlying array data used by the flex array.
The array specifications. An array of spec components.
Is this flex array transposed?
Public Class Methods
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
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
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
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
The version of this class. “<major>.<minor>.<step>”
# File lib/flex_array.rb, line 27 def self.version FlexArray::VERSION end
Public Instance Methods
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
Make FlexArrays comparable with the compariositality method.
# File lib/flex_array.rb, line 73 def <=>(other) @array_data <=> other.array_data end
Are these FlexArrays equal?
# File lib/flex_array.rb, line 68 def ==(other) self.compatible?(other) && @array_data == other.array_data end
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
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
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
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
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
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
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
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
The number of dimensions in this array.
# File lib/flex_array.rb, line 50 def dimensions @array_specs.spec_dimensions end
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
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
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
Is this flex array empty?
# File lib/flex_array.rb, line 78 def empty? length == 0 end
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
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
The total number of elements in this array.
# File lib/flex_array.rb, line 43 def length @array_specs.spec_count end
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
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
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
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
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
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
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
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
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
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
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
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
Return this flex array as a flex array!
# File lib/flex_array.rb, line 63 def to_flex_array self end
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 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
The version of the class of this instance.
# File lib/flex_array.rb, line 32 def version FlexArray::VERSION end
Private Instance Methods
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
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
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
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 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
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
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 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