flex_array 0.2.0 → 0.3.0

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.
@@ -0,0 +1,18 @@
1
+ #* flex_array_forever.rb - Forever looping support for the flexible array class.
2
+ class FlexArray
3
+ #A helper class that encapsulates the idea of looping without end!
4
+ class ForEver
5
+ #Do something until false == true ;-)
6
+ def times(&block)
7
+ loop {block.call}
8
+ end
9
+
10
+ #Convert an eternity into an integer. ;-)
11
+ def to_i
12
+ nil
13
+ end
14
+ end
15
+
16
+ #Create a forever constant for use where infinite looping is needed.
17
+ FOREVER = ForEver.new
18
+ end
@@ -2,20 +2,20 @@
2
2
  class FlexArray
3
3
  #Retrieve the selected data from the \FlexArray.
4
4
  #<br>Parameters
5
- #* indexes - An array with as many entries as the flexible array has
5
+ #* indexes - An array with as many entries as the flexible array has
6
6
  # dimensions. These entries may be an integer that is in the bounds of
7
7
  # the range limit of that dimension, or it can be a range where the start
8
- # and end of the range are in the bounds of the range limit of that
8
+ # and end of the range are in the bounds of the range limit of that
9
9
  # dimension, or it can be the symbol :all for all of the possible values
10
10
  # of that dimension.
11
11
  #<br>Returns
12
12
  #* The data selected by the index or an array of data if the index selects
13
- # more than one cell.
14
- #<br>Note
13
+ # more than one cell.
14
+ #<br>Note
15
15
  #* If the indexes parameter equals a single :all this maps to all
16
16
  # elements of the array, regardless of its dimensionality.
17
17
  def [](*indexes)
18
- validate_all_or_index_count(indexes)
18
+ validate_index_count(indexes)
19
19
  result = []
20
20
  process_indexes(indexes) {|_index, posn| result << @array_data[posn]}
21
21
  result.length == 1 ? result[0] : result
@@ -23,20 +23,22 @@ class FlexArray
23
23
 
24
24
  #Store the value data into the \FlexArray.
25
25
  #<br>Parameters
26
- #* indexes - An array with as many entries as the flexible array has
26
+ #* indexes - An array with as many entries as the flexible array has
27
27
  # dimensions. These entries may be an integer that is in the bounds of
28
28
  # the range limit of that dimension, or it can be a range where the start
29
- # and end of the range are in the bounds of the range limit of that
29
+ # and end of the range are in the bounds of the range limit of that
30
30
  # dimension, or it can be the symbol :all for all of the possible values
31
- # of that dimension.
31
+ # of that dimension.
32
32
  #* value - A value to be stored in the selected array entries.
33
33
  #<br>Returns
34
34
  #* The value stored in the array.
35
- #<br>Note
35
+ #<br>Note
36
36
  #* If the indexes parameter equals a single :all this maps to all
37
37
  # elements of the array, regardless of its dimensionality.
38
38
  def []=(*indexes, value)
39
- validate_all_or_index_count(indexes)
40
- process_indexes(indexes) {|_index, posn| @array_data[posn] = value}
39
+ validate_index_count(indexes)
40
+ source = value.in_array.cycle
41
+ process_indexes(indexes) {|_index, posn| @array_data[posn] = source.next}
42
+ value
41
43
  end
42
44
  end
@@ -2,34 +2,26 @@
2
2
  class FlexArray
3
3
  #Construct a flexible array object.
4
4
  #<br>Parameters
5
- #* array_specs - The specification of the flex array subscript limits.
6
- # These can either be integers, in which case the limits are 0...n or
7
- # they can be ranges of integers, in which case the limits are those
8
- # of the range, or they can be an array of integers and/or ranges of
5
+ #* array_specs - The specification of the flex array subscript limits.
6
+ # These can either be integers, in which case the limits are 0...n or
7
+ # they can be ranges of integers, in which case the limits are those
8
+ # of the range, or they can be an array of integers and/or ranges of
9
9
  # integers to represent arrays of more than one dimension.
10
10
  #* default - The optional default value. Defaults to nil.
11
11
  #* init_block - An optional initialization block. The return values from this
12
12
  # block are used to initialize the array. This overrides the default value.
13
13
  #<br>Block Arguments
14
- #* index - An array with one element per dimension with the fully qualified
14
+ #* index - An array with one element per dimension with the fully qualified
15
15
  # index of the element being accessed. Note that the same array is passed
16
16
  # in each time the block is called, so, if this parameter is to stored
17
17
  # anywhere, it's best that it be cloned or duplicated first.
18
18
  def initialize(array_specs, default=nil, &init_block)
19
- @array_specs, @count, @transposed = [], 1, false
20
-
21
- #Parse the array limits.
22
- array_specs.in_array.reverse_each do |spec|
23
- @array_specs.insert(0, spec.to_spec_component(@count))
24
- @count *= @array_specs[0].span
25
- end
26
-
27
- #Cache the dimensionality of the flex array.
28
- @dimensions = @array_specs.length
19
+ @array_specs = SpecArray.new(array_specs.in_array)
20
+ @transposed = false
29
21
 
30
22
  #Allocate the data for the array.
31
- @array_data = Array.new(@count, default)
32
-
23
+ @array_data = Array.new(@array_specs.spec_count, default)
24
+
33
25
  #Set up the array with the optional init_block.
34
26
  if init_block
35
27
  process_all {|index, posn| @array_data[posn] = init_block.call(index)}
@@ -39,6 +31,10 @@ class FlexArray
39
31
  alias_method :shallow_dup, :dup
40
32
 
41
33
  #Create a duplicate of this array.
34
+ #<br>Warning
35
+ #* The rdoc tool messes this up. shallow_dup is NOT an alias for this
36
+ # dup, but rather the original system implementation of dup. This dup,
37
+ # overrides the default dup and calls shallow_dup as part of its processing.
42
38
  def dup
43
39
  other = self.shallow_dup
44
40
  other.array_specs = @array_specs.dup
@@ -48,12 +44,12 @@ class FlexArray
48
44
 
49
45
  #Construct a \FlexArray using other as a template or data source.
50
46
  #<br>Parameters:
51
- #* array_specs - The specification of the flex array subscript limits.
52
- # These can either be integers, in which case the limits are 0...n or
53
- # they can be ranges of integers, in which case the limits are those
54
- # of the range, or they can be an array of integers and/or ranges of
47
+ #* array_specs - The specification of the flex array subscript limits.
48
+ # These can either be integers, in which case the limits are 0...n or
49
+ # they can be ranges of integers, in which case the limits are those
50
+ # of the range, or they can be an array of integers and/or ranges of
55
51
  # integers to represent arrays of more than one dimension.
56
- #* other - The array or flex array that is the source of the data.
52
+ #* other - The array or flex array that is the source of the data.
57
53
  #<br>Note:
58
54
  #* To make a copy of a flex array, use dup instead.
59
55
  def self.new_from(array_specs, other)
@@ -61,27 +57,27 @@ class FlexArray
61
57
  FlexArray.new(array_specs) {iterator.next}
62
58
  end
63
59
 
64
- #Construct a \FlexArray using a all or a portion of portion of another
60
+ #Construct a \FlexArray using a all or a portion of portion of another
65
61
  #\FlexArray as a data source.
66
62
  #<br>Parameters
67
- #* array_specs - The specification of the flex array subscript limits.
68
- # These can either be integers, in which case the limits are 0...n or
69
- # they can be ranges of integers, in which case the limits are those
70
- # of the range, or they can be an array of integers and/or ranges of
63
+ #* array_specs - The specification of the flex array subscript limits.
64
+ # These can either be integers, in which case the limits are 0...n or
65
+ # they can be ranges of integers, in which case the limits are those
66
+ # of the range, or they can be an array of integers and/or ranges of
71
67
  # integers to represent arrays of more than one dimension.
72
- #* other - The flex array source of the data used to build this flex
73
- # array. If no limits or other_indexes are specified, the limits of the
68
+ #* other - The flex array source of the data used to build this flex
69
+ # array. If no limits or other_indexes are specified, the limits of the
74
70
  # other array are used instead.
75
- #* selection - The indexes of the required sub-set of data to use
71
+ #* selection - The indexes of the required sub-set of data to use
76
72
  # in setting up the new array.
77
73
  #<br>Notes:
78
- #* If the entire source array is to be used, use new_from instead.
74
+ #* If the entire source array is to be used, use new_from instead.
79
75
  #* To make a copy of a flex array, use dup instead.
80
76
  def self.new_from_selection(array_specs, other, selection)
81
- iterator = other.cycle(selection)
77
+ iterator = other.select_cycle(selection)
82
78
  FlexArray.new(array_specs) {iterator.next}
83
79
  end
84
-
80
+
85
81
  #Construct a \FlexArray as a duplicate of a source array or flex array.
86
82
  #<br>Parameters
87
83
  #* other - the array or flex array that is the source.
@@ -93,6 +89,6 @@ class FlexArray
93
89
  result = FlexArray.new(0)
94
90
  result.array_specs = other.array_specs.dup
95
91
  result.array_data = other.array_data
96
- result.update_count_and_dimensions
92
+ result
97
93
  end
98
94
  end
@@ -4,27 +4,27 @@ class FlexArray
4
4
  #Process a \FlexArray index array. This is the heart of the \FlexArray
5
5
  #indexing process.
6
6
  #<br>Parameters
7
- #* indexes - An array with as many entries as the flexible array has
7
+ #* indexes - An array with as many entries as the flexible array has
8
8
  # dimensions. See [] for more details. Note that since indexes is NOT
9
9
  # a splat parameter, it must be passed as an array explicitly. If passed
10
10
  # in as [:all] all elements of the array are processed.
11
11
  #* block - a block to be called for each value selected by this process.
12
12
  #<br>Block Arguments
13
- #* current - An array with one element per dimension with the fully qualified
14
- # index of the element being accessed.
13
+ #* current - An array with one element per dimension with the fully qualified
14
+ # index of the element being accessed.
15
15
  #* posn - The position of the element being accessed in the data array.
16
16
  def process_indexes(indexes, &block)
17
- current = Array.new(@dimensions, 0)
18
-
17
+ current = Array.new(dimensions, 0)
18
+
19
19
  if indexes == [:all]
20
20
  process_all_worker(0, 0, current, &block)
21
21
  else
22
22
  specs = @array_specs.each
23
-
23
+
24
24
  checked = indexes.collect do |index|
25
25
  index.to_index_range(specs.next)
26
26
  end
27
-
27
+
28
28
  process_indexes_worker(0, 0, checked, current, &block)
29
29
  end
30
30
  end
@@ -32,71 +32,76 @@ class FlexArray
32
32
  #The worker bee for process_indexes.
33
33
  #<br>Parameters
34
34
  #* depth - The index of the dimension being processed.
35
- #* posn - The partially computed position of the element being accessed
35
+ #* posn - The partially computed position of the element being accessed
36
36
  # in the data array.
37
- #* indexes - An array of array indexes. See the method [] for details on
37
+ #* indexes - An array of array indexes. See the method [] for details on
38
38
  # what sorts of things these can be.
39
39
  #* current - The partial fully qualified index of the element being accessed.
40
40
  #* block - A block to be called for each value selected by this process.
41
41
  #<br>Block Arguments
42
- #* current - An array with one element per dimension with the fully qualified
43
- # index of the element being accessed.
42
+ #* current - An array with one element per dimension with the fully qualified
43
+ # index of the element being accessed.
44
44
  #* posn - The position of the element being accessed in the data array.
45
45
  #<br>Note
46
46
  #This is a recursive function. The recursion runs for index in 0..#dimensions
47
+ #<br>Endemic Code Smells
48
+ # :reek:LongParameterList
47
49
  def process_indexes_worker(depth, posn, indexes, current, &block)
48
- if depth == @dimensions
49
- block.call(current, posn)
50
+ if depth == dimensions #Is there more work to do?
51
+ block.call(current, posn) #Index ready, call the block.
50
52
  else
51
- spec = @array_specs[depth]
52
- range = indexes[depth]
53
- posn += spec.index_step(range.min)
54
- stride = spec.stride
55
-
56
- range.each do |index|
57
- current[depth] = index
58
- process_indexes_worker(depth+1, posn, indexes, current, &block)
59
- posn += stride
53
+ spec = @array_specs[depth] #Get the current specification.
54
+ min, stride = spec.min, spec.stride #Extract the relevant info.
55
+
56
+ indexes[depth].each do |index| #Iterate over the range.
57
+ current[depth] = index #Update the current index.
58
+ #Process the next component in the array index.
59
+ process_indexes_worker(depth+1,
60
+ posn + (index-min) * stride,
61
+ indexes,
62
+ current,
63
+ &block)
60
64
  end
61
65
  end
62
66
  end
63
-
67
+
64
68
  #Special case where all of the array is being processed.
65
69
  #<br>Parameters
66
70
  #* block - A block to be called for each value selected by this process.
67
71
  #<br>Block Arguments
68
- #* current - An array with one element per dimension with the fully qualified
69
- # index of the element being accessed.
72
+ #* current - An array with one element per dimension with the fully qualified
73
+ # index of the element being accessed.
70
74
  #* posn - The position of the element being accessed in the data array.
71
75
  def process_all(&block)
72
- current = Array.new(@dimensions, 0)
76
+ current = Array.new(dimensions, 0)
73
77
  process_all_worker(0, 0, current, &block)
74
78
  end
75
-
79
+
76
80
  #The worker bee for process_all.
77
81
  #<br>Parameters
78
82
  #* depth - The index of the dimension being processed.
79
- #* posn - The partially computed position of the element being accessed
83
+ #* posn - The partially computed position of the element being accessed
80
84
  # in the data array.
81
85
  #* current - The partial fully qualified index of the element being accessed.
82
86
  #* block - A block to be called for each value selected by this process.
83
87
  #<br>Block Arguments
84
- #* current - An array with one element per dimension with the fully qualified
85
- # index of the element being accessed.
88
+ #* current - An array with one element per dimension with the fully qualified
89
+ # index of the element being accessed.
86
90
  #* posn - The position of the element being accessed in the data array.
87
91
  #<br>Note
88
92
  #This is a recursive function. The recursion runs for index in 0..#dimensions
89
93
  def process_all_worker(depth, posn, current, &block)
90
- if depth == @dimensions
91
- block.call(current, posn)
94
+ if depth == dimensions #Is there more work to do?
95
+ block.call(current, posn) #Index ready, call the block.
92
96
  else
93
- spec = @array_specs[depth]
97
+ spec = @array_specs[depth] #Get the current specification.
94
98
  stride = spec.stride
95
-
96
- spec.each do |index|
97
- current[depth] = index
99
+
100
+ spec.each do |index| #Iterate over the range.
101
+ current[depth] = index #Update the current index.
102
+ #Process the next component in the array specification.
98
103
  process_all_worker(depth+1, posn, current, &block)
99
- posn += stride
104
+ posn += stride #Step to the next position.
100
105
  end
101
106
  end
102
107
  end
@@ -1,12 +1,12 @@
1
1
  #* flex_array_reshape.rb - The flexible array class reshape and related methods.
2
2
  class FlexArray
3
- #Return a copy of this \FlexArray, recast in a new shape, dropping or
3
+ #Return a copy of this \FlexArray, recast in a new shape, dropping or
4
4
  #repeating data elements as required.
5
5
  #<br>Parameters
6
- #* array_specs - The specification of the flex array subscript limits.
7
- # These can either be integers, in which case the limits are 0...n or
8
- # they can be ranges of integers, in which case the limits are those
9
- # of the range, or they can be an array of integers and/or ranges of
6
+ #* array_specs - The specification of the flex array subscript limits.
7
+ # These can either be integers, in which case the limits are 0...n or
8
+ # they can be ranges of integers, in which case the limits are those
9
+ # of the range, or they can be an array of integers and/or ranges of
10
10
  # integers to represent arrays of more than one dimension.
11
11
  #<br>Returns
12
12
  #* The reshaped flexible array.
@@ -15,31 +15,23 @@ class FlexArray
15
15
  FlexArray.new(array_specs) {iterator.next}
16
16
  end
17
17
 
18
- #Recast this \FlexArray in a new shape, dropping or
18
+ #Recast this \FlexArray in a new shape, dropping or
19
19
  #repeating data elements as required.
20
20
  #<br>Parameters
21
- #* array_specs - The specification of the flex array subscript limits.
22
- # These can either be integers, in which case the limits are 0...n or
23
- # they can be ranges of integers, in which case the limits are those
24
- # of the range, or they can be an array of integers and/or ranges of
21
+ #* array_specs - The specification of the flex array subscript limits.
22
+ # These can either be integers, in which case the limits are 0...n or
23
+ # they can be ranges of integers, in which case the limits are those
24
+ # of the range, or they can be an array of integers and/or ranges of
25
25
  # integers to represent arrays of more than one dimension.
26
26
  #<br>Returns
27
27
  #* The reshaped, flexible array.
28
28
  def reshape!(array_specs)
29
- iterator = @array_data.cycle
30
- temp = FlexArray.new(array_specs) {iterator.next}
31
- @array_specs, @array_data, @dimensions =
32
- temp.array_specs, temp.array_data, temp.dimensions
29
+ temp = self.reshape(array_specs)
30
+ @array_specs, @array_data = temp.array_specs, temp.array_data
33
31
  self
34
32
  end
35
33
 
36
- #Unravel the multi-dimensional structure, and any contained arrays into
37
- #a simple array.
38
- def flatten
39
- @array_data.flatten
40
- end
41
-
42
- #Convert the flex array to a simple array. Contained arrays are not affected.
34
+ #Convert the flex array to a simple array. Contained arrays are not affected.
43
35
  def to_a
44
36
  @array_data.dup
45
37
  end
@@ -7,11 +7,24 @@ class FlexArray
7
7
  #<br>Note
8
8
  #* Transposing an array disables the speed-up for processing the array with
9
9
  # an index of [:all].
10
- def transpose(dim_a, dim_b)
10
+ def transpose!(dim_a, dim_b)
11
11
  validate_dimension(dim_a)
12
12
  validate_dimension(dim_b)
13
13
  @array_specs[dim_a], @array_specs[dim_b] = @array_specs[dim_b], @array_specs[dim_a]
14
- @transposed = true
14
+ @transposed = @array_specs.transposed?
15
15
  self
16
16
  end
17
+
18
+ #Return a reference to this array's data with the specified dimensions
19
+ #transposed. This may change the "shape" of the array if the transposed
20
+ #dimensions were of different limits.
21
+ #<br>Returns
22
+ #* The flex array transposed.
23
+ #<br>Note
24
+ #* Transposing an array disables the speed-up for processing the array with
25
+ # an index of [:all].
26
+ def transpose(dim_a, dim_b)
27
+ FlexArray.new_from_array(self).transpose!(dim_a, dim_b)
28
+ end
29
+
17
30
  end
@@ -7,31 +7,31 @@ class FlexArray
7
7
  #* true if the arrays are compatible.
8
8
  def compatible?(other)
9
9
  @array_specs == other.array_specs
10
+ rescue Exception
11
+ false
10
12
  end
11
13
 
12
14
  private
13
-
15
+
14
16
  #Validate the dimensionality of the indexes passed in.
15
17
  #<br>Parameters
16
18
  #* indexes - An array of indexes to be validated.
17
- #<br>Returns
18
- #* true if the indexes value is [:all] and the array is not transposed.
19
19
  #<br>Exceptions
20
20
  #* ArgumentError - raised on invalid dimensionality.
21
- def validate_all_or_index_count(indexes)
22
- if indexes == [:all]
23
- !@transposed
24
- elsif @dimensions != indexes.length
25
- fail ArgumentError, "Incorrect number of indexes: #{@dimensions} expected."
21
+ def validate_index_count(indexes)
22
+ unless indexes == [:all]
23
+ if dimensions != indexes.length
24
+ fail ArgumentError, "Incorrect number of indexes: #{dimensions} expected."
25
+ end
26
26
  end
27
27
  end
28
-
28
+
29
29
  #Is this a valid dimension selector?
30
30
  #<br>Exceptions
31
31
  #* ArgumentError - raised on invalid dimension.
32
32
  def validate_dimension(dim)
33
- unless (0...@dimensions) === dim
34
- fail ArgumentError, "Invalid dimesnsion selector: #{dim}"
33
+ unless (0...dimensions) === dim
34
+ fail ArgumentError, "Invalid dimension selector: #{dim}"
35
35
  end
36
36
  end
37
37
  end