n-array 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/narray.rb +204 -0
- metadata +46 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a707fc3fefdb36a781b6e95cf03bd682499310a0
|
4
|
+
data.tar.gz: e6810c8c7e1bd895f100f86d9ef05abe888e0318
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 15c6217c2d7f9fe22ed77ea15403181dc598334314173a297d87b538a26c4823d6ff21da35dd6d3014d03e17a6b20a1ae26531d09238a4ccb6efba6b9a3795b5
|
7
|
+
data.tar.gz: 4ab6714b75861f9a3bf8a473bd6ef5b97e81e94b9fd8a944a9ef509feffaaa9786a6bbe4b090dff8f5a2a3f42beea16b226f504b00175af7aef39434aa74516f
|
data/lib/narray.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
# Multidimensional array for Ruby
|
2
|
+
|
3
|
+
class NArray < Array
|
4
|
+
attr_reader :dimensions
|
5
|
+
def initialize dimensions = nil, *values, &blck
|
6
|
+
# Make a difference between no arguments and nil
|
7
|
+
values = NArray::_extract values
|
8
|
+
|
9
|
+
# In case the only parameter is a NArray, duplicate it
|
10
|
+
if dimensions.is_a? NArray and !NArray::_arg? values
|
11
|
+
self.replace dimensions.dup
|
12
|
+
|
13
|
+
# In case the parameter is an array, multiple possibilities
|
14
|
+
elsif dimensions.is_a? Array
|
15
|
+
# 1) The array decribes dimensions and provide a default value to fill in the blanks
|
16
|
+
if NArray.is_valid_description? dimensions and (NArray::_arg? values or block_given?)
|
17
|
+
# then we build the n-array recursively and fill the values with what has been provided
|
18
|
+
@dimensions = dimensions.length
|
19
|
+
@dimensions == 1 ?
|
20
|
+
# A little detour to avoid warnings on nil values
|
21
|
+
!NArray::_arg?(values) ? super(dimensions[0], &blck) : super(dimensions[0], values) :
|
22
|
+
super( [*0...dimensions[0]].map { NArray.new(dimensions.drop(1), values, &blck) } )
|
23
|
+
|
24
|
+
# 2) the array does not provide a default value
|
25
|
+
elsif !NArray::_arg? values and !block_given?
|
26
|
+
# then we create a NArray fitting the litteral given
|
27
|
+
@dimensions = NArray.count_dimensions dimensions #inefficient but GTD
|
28
|
+
@dimensions == 1 ?
|
29
|
+
super(dimensions) : # giving a block here has no effect with mri and the doc doesn't say anything
|
30
|
+
super(dimensions.map { |e| NArray.new(e) })
|
31
|
+
|
32
|
+
# 3) the array is not a valid description but a default value is given, i.e. user mistake. Scold him!
|
33
|
+
else
|
34
|
+
raise RuntimeError, "#{dimensions} is not a valid description:
|
35
|
+
An array of striclty positive Integers is expected"
|
36
|
+
end
|
37
|
+
|
38
|
+
# In case the dimension is valid
|
39
|
+
elsif NArray.is_valid_dimension? dimensions
|
40
|
+
@dimensions = dimensions
|
41
|
+
if dimensions == 1
|
42
|
+
super([], &blck)
|
43
|
+
else
|
44
|
+
super([NArray.new(dimensions - 1, values, &blck)])
|
45
|
+
end
|
46
|
+
|
47
|
+
# Empty constructor
|
48
|
+
elsif dimensions.nil? and !NArray::_arg? values
|
49
|
+
super(&blck)
|
50
|
+
|
51
|
+
# Bad user, bad
|
52
|
+
else
|
53
|
+
raise RuntimeError \
|
54
|
+
"Invalid dimension (expecting an Integer or array of Integer all strictly positives, got #{dimensions}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns an array of the lengths of each dimension
|
59
|
+
def lengths
|
60
|
+
#NArray.calculate_dimensions(self) # doesn't work for some weird reason
|
61
|
+
dimensions == 1 ? [length] : [length, *self[0].lengths]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the length of the dimension given as parameter
|
65
|
+
#
|
66
|
+
# Starts at 0
|
67
|
+
def length d = 0
|
68
|
+
raise "Expecting positive Integer < #{dimensions}, got #{d}" unless d < dimensions and d >= 0
|
69
|
+
d == 0 ? super() : self[0].length(d - 1)
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
# Fetch the value at the position of the arguments
|
75
|
+
#
|
76
|
+
# In case the argument contains nil values, returns a NArray of the elements
|
77
|
+
# satisfying the coordinates given by the arguments (to-do)
|
78
|
+
def [] *pos
|
79
|
+
raise "1..#{dimensions} arguments expected, given #{pos.length}" if pos.length > dimensions or pos.length == 0
|
80
|
+
pos.length == 1 ? super(*pos) : super(pos[0])[*pos.drop(1)]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Sets the value at the position of the arguments
|
84
|
+
def []= *pos, v
|
85
|
+
raise "#{dimensions} arguments expected, given #{pos.length}" if pos.length != dimensions
|
86
|
+
pos.length == 1 ? super(*pos, v) : self[pos[0]][*pos.drop(1)] = v
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the total number of elements in the n-array
|
90
|
+
def size
|
91
|
+
lengths.reduce(&:*)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Iterate over the elements of the n-array applying the given block to each element
|
95
|
+
def each &blck
|
96
|
+
if dimensions > 1
|
97
|
+
super() do |e|
|
98
|
+
e.each(&blck)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
super(&blck)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Iterate over the elements of the n-array apply the given bloc and collect
|
106
|
+
# the results into a nested collection of arrays identical to the structure of the caller
|
107
|
+
def map &blck
|
108
|
+
if dimensions > 1
|
109
|
+
super() do |e|
|
110
|
+
e.map(&blck)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
super(&blck)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# See #map
|
117
|
+
def collect
|
118
|
+
map
|
119
|
+
end
|
120
|
+
|
121
|
+
class << self
|
122
|
+
# Returns the number of dimensions that can be generated from the argument while keeping the array well-formed
|
123
|
+
#
|
124
|
+
# Checks the maximum level of nesting so that any n-vector {v1, v2...vn} with 0 <= vm < length(m)
|
125
|
+
# correctly refers to an element in the structure
|
126
|
+
def count_dimensions array
|
127
|
+
array.class == NArray ? array.dimensions : calculate_dimensions(array).length
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns an array of lengths for an array (only works with Array, don't ask me why)
|
131
|
+
#
|
132
|
+
# Each dimension in a n-array has a maximum size, those are collected and ordered into an array,
|
133
|
+
# the first being the top array, and the last the deepest group
|
134
|
+
def calculate_dimensions array
|
135
|
+
_count(array).take_while { |e| e >= 0 }
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# Check whether the argument is a valid dimension
|
140
|
+
#
|
141
|
+
# Returns true if dimensions is a strictly positive Integer, false otherwise
|
142
|
+
def is_valid_dimension? dimensions
|
143
|
+
dimensions.is_a? Integer and dimensions > 0
|
144
|
+
end
|
145
|
+
|
146
|
+
# Check whether the argument is a valid description of dimensions
|
147
|
+
#
|
148
|
+
# Returns true if the argument is an Array of values satisfying is_valid_dimension?, false otherwise
|
149
|
+
def is_valid_description? dimensions
|
150
|
+
dimensions.is_a? Array and dimensions.all? { |e| is_valid_dimension? e }
|
151
|
+
end
|
152
|
+
|
153
|
+
# Create a n-array fitting the given description
|
154
|
+
def[] *description
|
155
|
+
NArray.new(description)
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
def _count array, n = [], d = 0
|
160
|
+
unless array.is_a? Array or array.is_a? NArray # if not an array, set the dimension as beeing incoherent
|
161
|
+
n[d] = -1
|
162
|
+
return n # and abort
|
163
|
+
end
|
164
|
+
|
165
|
+
# if length <= depth it means this dimension hasn't been explored yet,
|
166
|
+
# so we set it at the first value we encounter, which will not change
|
167
|
+
# if the array is well formed
|
168
|
+
n << array.length if n.length <= d
|
169
|
+
|
170
|
+
if array.length != n[d] #or n[d] < 0 # the second part should never
|
171
|
+
# be executed since array.length >= 0 (
|
172
|
+
# if n[d] < 0 then the first test automatically fails)
|
173
|
+
# The dimension is in an incoherent state, abort
|
174
|
+
n[d] = -1
|
175
|
+
return n
|
176
|
+
end
|
177
|
+
|
178
|
+
# At this point the array is still in a coherent state,
|
179
|
+
# we just need to check sub elements unless we already know
|
180
|
+
# that we can't proceed further because of previous results
|
181
|
+
array.each do |e|
|
182
|
+
if n.length > d + 1 and n[d + 1] < 0 # In case the next element has
|
183
|
+
# already been decided to be incoherent
|
184
|
+
return n # Abort
|
185
|
+
end
|
186
|
+
_count e, n, d + 1
|
187
|
+
end
|
188
|
+
n
|
189
|
+
end
|
190
|
+
|
191
|
+
public
|
192
|
+
def _extract args
|
193
|
+
raise ParameterError, "Expected 1..2 arguments, got #{args.length + 1}" if args.length > 1
|
194
|
+
args.length == 1 ? args[0] : NArray::EmptyArgument.new
|
195
|
+
end
|
196
|
+
|
197
|
+
def _arg? arg
|
198
|
+
!arg.is_a? NArray::EmptyArgument
|
199
|
+
end
|
200
|
+
end
|
201
|
+
private
|
202
|
+
class EmptyArgument
|
203
|
+
end
|
204
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: n-array
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sylvain Leclercq
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-02 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Manipulating nested arrays in Ruby is a pain and there doesn't seem to
|
14
|
+
be any general purpose solutions (i.e. not math-oriented) available so here is a
|
15
|
+
shot at it.
|
16
|
+
email: maisbiensurqueoui@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/narray.rb
|
22
|
+
homepage: http://www.github.com/de-passage/narray
|
23
|
+
licenses:
|
24
|
+
- MIT
|
25
|
+
metadata: {}
|
26
|
+
post_install_message:
|
27
|
+
rdoc_options: []
|
28
|
+
require_paths:
|
29
|
+
- lib
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
requirements: []
|
41
|
+
rubyforge_project:
|
42
|
+
rubygems_version: 2.4.8
|
43
|
+
signing_key:
|
44
|
+
specification_version: 4
|
45
|
+
summary: A n-dimensional storage structure
|
46
|
+
test_files: []
|