class NArray

Multidimensional array for Ruby

Attributes

dimensions[R]

Public Class Methods

[](*description) click to toggle source

Create a n-array fitting the given description

# File lib/n-array.rb, line 154
def[] *description
        NArray.new(description)
end
_arg?(arg) click to toggle source
# File lib/n-array.rb, line 197
def _arg? arg
        !arg.is_a? NArray::EmptyArgument
end
_extract(args) click to toggle source
# File lib/n-array.rb, line 192
def _extract args
        raise ParameterError, "Expected 1..2 arguments, got #{args.length + 1}" if args.length > 1  
        args.length == 1 ? args[0] : NArray::EmptyArgument.new
end
calculate_dimensions(array) click to toggle source

Returns an array of lengths for an array (only works with Array, don’t ask me why)

Each dimension in a n-array has a maximum size, those are collected and ordered into an array, the first being the top array, and the last the deepest group

# File lib/n-array.rb, line 134
def calculate_dimensions array
        _count(array).take_while { |e| e >= 0 }
end
count_dimensions(array) click to toggle source

Returns the number of dimensions that can be generated from the argument while keeping the array well-formed

Checks the maximum level of nesting so that any n-vector {v1, v2…vn} with 0 <= vm < length(m) correctly refers to an element in the structure

# File lib/n-array.rb, line 126
def count_dimensions array
        array.class == NArray ? array.dimensions : calculate_dimensions(array).length
end
is_valid_description?(dimensions) click to toggle source

Check whether the argument is a valid description of dimensions

Returns true if the argument is an Array of values satisfying is_valid_dimension?, false otherwise

# File lib/n-array.rb, line 149
def is_valid_description? dimensions
        dimensions.is_a? Array and dimensions.all? { |e| is_valid_dimension? e }
end
is_valid_dimension?(dimensions) click to toggle source

Check whether the argument is a valid dimension

Returns true if dimensions is a strictly positive Integer, false otherwise

# File lib/n-array.rb, line 142
def is_valid_dimension? dimensions
        dimensions.is_a? Integer and dimensions > 0
end
new(dimensions = nil, *values, &blck) click to toggle source
Calls superclass method
# File lib/n-array.rb, line 5
def initialize dimensions = nil, *values, &blck
        # Make a difference between no arguments and nil
        values = NArray::_extract values

        # In case the only parameter is a NArray, duplicate it
        if dimensions.is_a? NArray and  !NArray::_arg? values
                self.replace dimensions.dup

        # In case the parameter is an array, multiple possibilities
        elsif dimensions.is_a? Array
                # 1) The array decribes dimensions and provide a default value to fill in the blanks
                if NArray.is_valid_description? dimensions and (NArray::_arg? values or block_given?)
                        # then we build the n-array recursively and fill the values with what has been provided
                        @dimensions = dimensions.length
                        @dimensions == 1 ?
                                # A little detour to avoid warnings on nil values
                                !NArray::_arg?(values) ? super(dimensions[0], &blck) : super(dimensions[0], values) :
                                super( [*0...dimensions[0]].map { NArray.new(dimensions.drop(1), values, &blck) } )

                # 2) the array does not provide a default value
                elsif !NArray::_arg? values and !block_given?
                        # then we create a NArray fitting the litteral given
                        @dimensions = NArray.count_dimensions dimensions #inefficient but GTD
                        @dimensions == 1 ?
                                super(dimensions) : # giving a block here has no effect with mri and the doc doesn't say anything
                                super(dimensions.map { |e| NArray.new(e) })

                # 3) the array is not a valid description but a default value is given, i.e. user mistake. Scold him!
                else
                        raise RuntimeError, "#{dimensions} is not a valid description: 
                        An array of striclty positive Integers is expected"
                end

        # In case the dimension is valid
        elsif NArray.is_valid_dimension? dimensions
                @dimensions = dimensions
                if dimensions == 1
                        super([], &blck)
                else 
                        super([NArray.new(dimensions - 1, values, &blck)])
                end

        # Empty constructor
        elsif dimensions.nil? and !NArray::_arg? values
                super(&blck)

        # Bad user, bad
        else
                raise RuntimeError \
                        "Invalid dimension (expecting an Integer or array of Integer all strictly positives, got #{dimensions}"
        end
end

Private Class Methods

_count(array, n = [], d = 0) click to toggle source
# File lib/n-array.rb, line 159
def _count array, n = [], d = 0
        unless array.is_a? Array or array.is_a? NArray # if not an array, set the dimension as beeing incoherent
                n[d] = -1
                return n # and abort
        end

        # if length <= depth it means this dimension hasn't been explored yet,
        # so we set it at the first value we encounter, which will not change
        # if the array is well formed
        n << array.length if n.length <= d

        if array.length != n[d] #or n[d] < 0  # the second part should never
                                                                                  # be executed since array.length >= 0 (
                                                                                  # if n[d] < 0 then the first test automatically fails)
                # The dimension is in an incoherent state, abort
                n[d] = -1
                return n
        end

        # At this point the array is still in a coherent state,
        # we just need to check sub elements unless we already know
        # that we can't proceed further because of previous results
        array.each do |e|
                if n.length > d + 1 and n[d + 1] < 0 # In case the next element has
                                                                                         # already been decided to be incoherent
                        return n # Abort
                end
                _count e, n, d + 1
        end
        n
end

Public Instance Methods

[](*pos) click to toggle source

Fetch the value at the position of the arguments

In case the argument contains nil values, returns a NArray of the elements satisfying the coordinates given by the arguments (to-do)

Calls superclass method
# File lib/n-array.rb, line 78
def [] *pos
        raise "1..#{dimensions} arguments expected, given #{pos.length}" if pos.length > dimensions or pos.length == 0
        pos.length == 1 ? super(*pos) : super(pos[0])[*pos.drop(1)]
end
[]=(*pos, v) click to toggle source

Sets the value at the position of the arguments

Calls superclass method
# File lib/n-array.rb, line 84
def []= *pos, v
        raise "#{dimensions} arguments expected, given #{pos.length}" if pos.length != dimensions
        pos.length == 1 ? super(*pos, v) : self[pos[0]][*pos.drop(1)] = v
end
collect() click to toggle source

See map

# File lib/n-array.rb, line 117
def collect
        map
end
each(&blck) click to toggle source

Iterate over the elements of the n-array applying the given block to each element

Calls superclass method
# File lib/n-array.rb, line 95
def each &blck
        if dimensions > 1
                super() do |e|
                        e.each(&blck)
                end
        else
                super(&blck)
        end
end
length(d = 0) click to toggle source

Returns the length of the dimension given as parameter

Starts at 0

Calls superclass method
# File lib/n-array.rb, line 67
def length d = 0
        raise "Expecting positive Integer < #{dimensions}, got #{d}" unless d < dimensions and d >= 0
        d == 0 ? super() : self[0].length(d - 1)
end
lengths() click to toggle source

Returns an array of the lengths of each dimension

# File lib/n-array.rb, line 59
def lengths
        #NArray.calculate_dimensions(self) # doesn't work for some weird reason
        dimensions == 1 ? [length] : [length, *self[0].lengths]
end
map(&blck) click to toggle source

Iterate over the elements of the n-array apply the given bloc and collect the results into a nested collection of arrays identical to the structure of the caller

Calls superclass method
# File lib/n-array.rb, line 107
def map &blck
        if dimensions > 1
                super() do |e|
                        e.map(&blck)
                end
        else
                super(&blck)
        end
end
size() click to toggle source

Returns the total number of elements in the n-array

# File lib/n-array.rb, line 90
def size
        lengths.reduce(&:*)
end