Class: NArray

Inherits:
Array
  • Object
show all
Defined in:
lib/n-array.rb

Overview

Multidimensional array for Ruby

Defined Under Namespace

Classes: EmptyArgument

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dimensions = nil, *values, &blck) ⇒ NArray

Returns a new instance of NArray.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 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

Instance Attribute Details

#dimensionsObject (readonly)

Returns the value of attribute dimensions.



4
5
6
# File 'lib/n-array.rb', line 4

def dimensions
  @dimensions
end

Class Method Details

.[](*description) ⇒ Object

Create a n-array fitting the given description



154
155
156
# File 'lib/n-array.rb', line 154

def[] *description
	NArray.new(description)
end

._arg?(arg) ⇒ Boolean

Returns:

  • (Boolean)


197
198
199
# File 'lib/n-array.rb', line 197

def _arg? arg
	!arg.is_a? NArray::EmptyArgument
end

._extract(args) ⇒ Object

Raises:

  • (ParameterError)


192
193
194
195
# 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) ⇒ Object

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



134
135
136
# File 'lib/n-array.rb', line 134

def calculate_dimensions array
	_count(array).take_while { |e| e >= 0 }
end

.count_dimensions(array) ⇒ Object

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 v2…vn with 0 <= vm < length(m) correctly refers to an element in the structure



126
127
128
# 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) ⇒ Boolean

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

Returns:

  • (Boolean)


149
150
151
# 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) ⇒ Boolean

Check whether the argument is a valid dimension

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

Returns:

  • (Boolean)


142
143
144
# File 'lib/n-array.rb', line 142

def is_valid_dimension? dimensions
	dimensions.is_a? Integer and dimensions > 0
end

Instance Method Details

#[](*pos) ⇒ Object

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)



78
79
80
81
# 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) ⇒ Object

Sets the value at the position of the arguments



84
85
86
87
# 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

#collectObject

See #map



117
118
119
# File 'lib/n-array.rb', line 117

def collect
	map
end

#each(&blck) ⇒ Object

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



95
96
97
98
99
100
101
102
103
# 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) ⇒ Object

Returns the length of the dimension given as parameter

Starts at 0



67
68
69
70
# 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

#lengthsObject

Returns an array of the lengths of each dimension



59
60
61
62
# 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) ⇒ Object

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



107
108
109
110
111
112
113
114
115
# 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

#sizeObject

Returns the total number of elements in the n-array



90
91
92
# File 'lib/n-array.rb', line 90

def size
	lengths.reduce(&:*)
end