Class: Immutable::Vector
- Inherits:
-
Object
- Object
- Immutable::Vector
- Includes:
- Enumerable
- Defined in:
- lib/immutable/vector.rb
Overview
A ‘Vector` is an ordered, integer-indexed collection of objects. Like Ruby’s ‘Array`, `Vector` indexing starts at zero and negative indexes count back from the end.
‘Vector` has a similar interface to `Array`. The main difference is methods that would destructively update an `Array` (such as #insert or #delete_at) instead return new `Vectors` and leave the existing one unchanged.
### Creating New Vectors
Immutable::Vector.new([:first, :second, :third])
Immutable::Vector[1, 2, 3, 4, 5]
### Retrieving Items from Vectors
vector = Immutable::Vector[1, 2, 3, 4, 5]
vector[0] # => 1
vector[-1] # => 5
vector[0,3] # => Immutable::Vector[1, 2, 3]
vector[1..-1] # => Immutable::Vector[2, 3, 4, 5]
vector.first # => 1
vector.last # => 5
### Creating Modified Vectors
vector.add(6) # => Immutable::Vector[1, 2, 3, 4, 5, 6]
vector.insert(1, :a, :b) # => Immutable::Vector[1, :a, :b, 2, 3, 4, 5]
vector.delete_at(2) # => Immutable::Vector[1, 2, 4, 5]
vector + [6, 7] # => Immutable::Vector[1, 2, 3, 4, 5, 6, 7]
Constant Summary collapse
- BLOCK_SIZE =
32- INDEX_MASK =
BLOCK_SIZE - 1
- BITS_PER_LEVEL =
5
Instance Attribute Summary collapse
-
#size ⇒ Integer
(also: #length)
readonly
Return the number of items in this ‘Vector`.
Class Method Summary collapse
-
.[](*items) ⇒ Vector
Create a new ‘Vector` populated with the given items.
-
.alloc(root, size, levels) ⇒ Vector
“Raw” allocation of a new ‘Vector`.
-
.empty ⇒ Vector
Return an empty ‘Vector`.
Instance Method Summary collapse
-
#*(times) ⇒ Vector
Repetition.
-
#+(other) ⇒ Vector
(also: #concat)
Return a new ‘Vector` built by concatenating this one with `other`.
-
#add(item) ⇒ Vector
(also: #<<, #push)
Return a new ‘Vector` with `item` added after the last occupied position.
-
#assoc(obj) ⇒ Object
Assumes all elements are nested, indexable collections, and searches through them, comparing ‘obj` with the first element of each nested collection.
-
#bsearch {|element| ... } ⇒ Object
Finds a value from this ‘Vector` which meets the condition defined by the provided block, using a binary search.
-
#clear ⇒ Vector
Return an empty ‘Vector` instance, of the same class as this one.
-
#combination(n) ⇒ self, Enumerator
When invoked with a block, yields all combinations of length ‘n` of items from the `Vector`, and then returns `self`.
-
#delete(obj) ⇒ Vector
Return a new ‘Vector` with all items which are equal to `obj` removed.
-
#delete_at(index) ⇒ Vector
Return a new ‘Vector` with the element at `index` removed.
-
#dig(key, *rest) ⇒ Object
Return the value of successively indexing into a nested collection.
-
#drop(n) ⇒ Vector
Drop the first ‘n` elements and return the rest in a new `Vector`.
-
#drop_while ⇒ Vector, Enumerator
Drop elements up to, but not including, the first element for which the block returns ‘nil` or `false`.
-
#dup ⇒ Vector
(also: #clone)
Return ‘self`.
-
#each(&block) ⇒ self, Enumerator
Call the given block once for each item in the vector, passing each item from first to last successively to the block.
-
#empty? ⇒ Boolean
Return ‘true` if this `Vector` contains no items.
-
#eql?(other) ⇒ Boolean
Return true if ‘other` has the same type and contents as this `Vector`.
-
#fetch(index, default = (missing_default = true)) ⇒ Object
Retrieve the value at ‘index` with optional default.
-
#fill(object, index = 0, length = nil) ⇒ Vector
Replace a range of indexes with the given object.
-
#first ⇒ Object
Return the first item in the ‘Vector`.
-
#flat_map ⇒ Vector
Return a new ‘Vector` with the concatenated results of running the block once for every element in this `Vector`.
-
#flatten(level = -1)) ⇒ Vector
Return a new ‘Vector` with all nested vectors and arrays recursively “flattened out”.
-
#get(index) ⇒ Object
(also: #at)
Retrieve the item at ‘index`.
-
#hash ⇒ Integer
See ‘Object#hash`.
-
#initialize(items = [].freeze) ⇒ Vector
constructor
A new instance of Vector.
-
#insert(index, *items) ⇒ Vector
Return a new ‘Vector` with the given values inserted before the element at `index`.
-
#last ⇒ Object
Return the last item in the ‘Vector`.
-
#map ⇒ Vector, Enumerator
(also: #collect)
Invoke the given block once for each item in the vector, and return a new ‘Vector` containing the values returned by the block.
- #marshal_dump ⇒ ::Array
- #marshal_load(array) ⇒ Object
-
#permutation(n = @size) ⇒ self, Enumerator
Yields all permutations of length ‘n` of items from the `Vector`, and then returns `self`.
-
#pop ⇒ Vector
Return a new ‘Vector` with the last element removed.
-
#product(*vectors) ⇒ Vector
Cartesian product or multiplication.
-
#rassoc(obj) ⇒ Object
Assumes all elements are nested, indexable collections, and searches through them, comparing ‘obj` with the second element of each nested collection.
-
#repeated_combination(n) ⇒ self, Enumerator
When invoked with a block, yields all repeated combinations of length ‘n` of items from the `Vector`, and then returns `self`.
-
#repeated_permutation(n = @size) ⇒ self, Enumerator
When invoked with a block, yields all repeated permutations of length ‘n` of items from the `Vector`, and then returns `self`.
-
#reverse ⇒ Vector
Return a new ‘Vector` with the same elements as this one, but in reverse order.
-
#reverse_each(&block) ⇒ self
Call the given block once for each item in the vector, from last to first.
-
#rindex(obj = (missing_arg = true)) ⇒ Integer
Find the index of an element, starting from the end of the vector.
-
#rotate(count = 1) ⇒ Vector
Return a new ‘Vector` with the same elements, but rotated so that the one at index `count` is the first element of the new vector.
-
#sample ⇒ Object
Return a randomly chosen item from this ‘Vector`.
-
#select {|element| ... } ⇒ Vector
(also: #find_all, #keep_if)
Return a new ‘Vector` containing all elements for which the given block returns true.
-
#set(index, item = yield(get(index))) ⇒ Vector
Return a new ‘Vector` with a new value at the given `index`.
-
#shift ⇒ Vector
Return a new ‘Vector` with the first element removed.
-
#shuffle ⇒ Vector
Return a new ‘Vector` with the same elements as this one, but randomly permuted.
-
#slice(arg, length = (missing_length = true)) ⇒ Object
(also: #[])
Return specific objects from the ‘Vector`.
-
#sort ⇒ Vector
Return a new ‘Vector` with the same items, but sorted.
-
#sort_by {|element| ... } ⇒ Vector
Return a new ‘Vector` with the same items, but sorted.
-
#take(n) ⇒ Vector
Return only the first ‘n` elements in a new `Vector`.
-
#take_while ⇒ Vector, Enumerator
Gather elements up to, but not including, the first element for which the block returns ‘nil` or `false`, and return them in a new `Vector`.
-
#to_a ⇒ Array
(also: #to_ary)
Return an ‘Array` with the same elements, in the same order.
-
#transpose ⇒ Vector
Assume all elements are vectors or arrays and transpose the rows and columns.
-
#uniq(&block) ⇒ Vector
Return a new ‘Vector` with no duplicate elements, as determined by `#hash` and `#eql?`.
-
#unshift(object) ⇒ Vector
Return a new ‘Vector` with `object` inserted before the first element, moving the other elements upwards.
-
#update_in(*key_path) {|value| ... } ⇒ Vector
Return a new ‘Vector` with a deeply nested value modified to the result of the given code block.
-
#values_at(*indices) ⇒ Vector
Return a new ‘Vector` with only the elements at the given `indices`, in the order specified by `indices`.
-
#zip(*others) ⇒ Vector
Combine two vectors by “zipping” them together.
Methods included from Enumerable
#<=>, #==, #compact, #each_index, #grep, #grep_v, #group_by, #inspect, #join, #partition, #pretty_print, #reject, #sum, #to_set
Methods included from Enumerable
Constructor Details
#initialize(items = [].freeze) ⇒ Vector
Returns a new instance of Vector.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/immutable/vector.rb', line 82 def initialize(items=[].freeze) items = items.to_a if items.size <= 32 items = items.dup.freeze if !items.frozen? @root, @size, @levels = items, items.size, 0 else root, size, levels = items, items.size, 0 while root.size > 32 root = root.each_slice(32).to_a levels += 1 end @root, @size, @levels = root.freeze, size, levels end freeze end |
Instance Attribute Details
#size ⇒ Integer (readonly) Also known as: length
Return the number of items in this ‘Vector`
50 51 52 |
# File 'lib/immutable/vector.rb', line 50 def size @size end |
Class Method Details
.[](*items) ⇒ Vector
Create a new ‘Vector` populated with the given items.
56 57 58 |
# File 'lib/immutable/vector.rb', line 56 def [](*items) new(items.freeze) end |
.alloc(root, size, levels) ⇒ Vector
“Raw” allocation of a new ‘Vector`. Used internally to create a new instance quickly after building a modified trie.
73 74 75 76 77 78 79 |
# File 'lib/immutable/vector.rb', line 73 def alloc(root, size, levels) obj = allocate obj.instance_variable_set(:@root, root) obj.instance_variable_set(:@size, size) obj.instance_variable_set(:@levels, levels) obj.freeze end |
.empty ⇒ Vector
Return an empty ‘Vector`. If used on a subclass, returns an empty instance of that class.
64 65 66 |
# File 'lib/immutable/vector.rb', line 64 def empty @empty ||= self.new end |
Instance Method Details
#*(times) ⇒ Vector
Repetition. Return a new ‘Vector` built by concatenating `times` copies of this one together.
778 779 780 781 782 783 |
# File 'lib/immutable/vector.rb', line 778 def *(times) return self.class.empty if times == 0 return self if times == 1 result = (to_a * times) result.is_a?(Array) ? self.class.new(result) : result end |
#+(other) ⇒ Vector Also known as: concat
Return a new ‘Vector` built by concatenating this one with `other`. `other` can be any object which is convertible to an `Array` using `#to_a`.
631 632 633 634 635 |
# File 'lib/immutable/vector.rb', line 631 def +(other) other = other.to_a other = other.dup if other.frozen? replace_suffix(@size, other) end |
#add(item) ⇒ Vector Also known as: <<, push
Return a new ‘Vector` with `item` added after the last occupied position.
132 133 134 |
# File 'lib/immutable/vector.rb', line 132 def add(item) update_root(@size, item) end |
#assoc(obj) ⇒ Object
Assumes all elements are nested, indexable collections, and searches through them, comparing ‘obj` with the first element of each nested collection. Return the first nested collection which matches, or `nil` if none is found. Behaviour is undefined when elements do not meet assumptions (i.e. are not indexable collections).
1263 1264 1265 1266 1267 1268 1269 |
# File 'lib/immutable/vector.rb', line 1263 def assoc(obj) each do |array| next if !array.respond_to?(:[]) return array if obj == array[0] end nil end |
#bsearch {|element| ... } ⇒ Object
Finds a value from this ‘Vector` which meets the condition defined by the provided block, using a binary search. The vector must already be sorted with respect to the block. See Ruby’s ‘Array#bsearch` for details, behaviour is equivalent.
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 |
# File 'lib/immutable/vector.rb', line 1157 def bsearch return enum_for(:bsearch) if not block_given? low, high, result = 0, @size, nil while low < high mid = (low + ((high - low) >> 1)) val = get(mid) v = yield val if v.is_a? Numeric if v == 0 return val elsif v > 0 high = mid else low = mid + 1 end elsif v == true result = val high = mid elsif !v low = mid + 1 else raise TypeError, "wrong argument type #{v.class} (must be numeric, true, false, or nil)" end end result end |
#clear ⇒ Vector
Return an empty ‘Vector` instance, of the same class as this one. Useful if you have multiple subclasses of `Vector` and want to treat them polymorphically.
1188 1189 1190 |
# File 'lib/immutable/vector.rb', line 1188 def clear self.class.empty end |
#combination(n) ⇒ self, Enumerator
When invoked with a block, yields all combinations of length ‘n` of items from the `Vector`, and then returns `self`. There is no guarantee about which order the combinations will be yielded.
If no block is given, an ‘Enumerator` is returned instead.
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 |
# File 'lib/immutable/vector.rb', line 857 def combination(n) return enum_for(:combination, n) if not block_given? return self if n < 0 || @size < n if n == 0 yield [] elsif n == 1 each { |item| yield [item] } elsif n == @size yield self.to_a else combos = lambda do |result,index,remaining| while @size - index > remaining if remaining == 1 yield result.dup << get(index) else combos[result.dup << get(index), index+1, remaining-1] end index += 1 end index.upto(@size-1) { |i| result << get(i) } yield result end combos[[], 0, n] end self end |
#delete(obj) ⇒ Vector
Return a new ‘Vector` with all items which are equal to `obj` removed. `#==` is used for checking equality.
504 505 506 |
# File 'lib/immutable/vector.rb', line 504 def delete(obj) select { |item| item != obj } end |
#delete_at(index) ⇒ Vector
Return a new ‘Vector` with the element at `index` removed. If the given `index` does not exist, return `self`.
400 401 402 403 404 405 406 |
# File 'lib/immutable/vector.rb', line 400 def delete_at(index) return self if index >= @size || index < -@size index += @size if index < 0 suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, []) replace_suffix(index, suffix.tap { |a| a.shift }) end |
#dig(key, *rest) ⇒ Object
Return the value of successively indexing into a nested collection. If any of the keys is not present, return ‘nil`.
291 292 293 294 295 296 297 298 |
# File 'lib/immutable/vector.rb', line 291 def dig(key, *rest) value = self[key] if rest.empty? || value.nil? value else value.dig(*rest) end end |
#drop(n) ⇒ Vector
Drop the first ‘n` elements and return the rest in a new `Vector`.
721 722 723 724 725 726 |
# File 'lib/immutable/vector.rb', line 721 def drop(n) return self if n == 0 return self.class.empty if n >= @size raise ArgumentError, "attempt to drop negative size" if n < 0 self.class.new(flatten_suffix(@root, @levels * BITS_PER_LEVEL, n, [])) end |
#drop_while ⇒ Vector, Enumerator
Drop elements up to, but not including, the first element for which the block returns ‘nil` or `false`. Gather the remaining elements into a new `Vector`. If no block is given, an `Enumerator` is returned instead.
750 751 752 753 |
# File 'lib/immutable/vector.rb', line 750 def drop_while return enum_for(:drop_while) if not block_given? self.class.new(super) end |
#dup ⇒ Vector Also known as: clone
Return ‘self`. Since this is an immutable object duplicates are equivalent.
1325 1326 1327 |
# File 'lib/immutable/vector.rb', line 1325 def dup self end |
#each(&block) ⇒ self, Enumerator
Call the given block once for each item in the vector, passing each item from first to last successively to the block. If no block is given, an ‘Enumerator` is returned instead.
457 458 459 460 461 |
# File 'lib/immutable/vector.rb', line 457 def each(&block) return to_enum unless block_given? traverse_depth_first(@root, @levels, &block) self end |
#empty? ⇒ Boolean
Return ‘true` if this `Vector` contains no items.
101 102 103 |
# File 'lib/immutable/vector.rb', line 101 def empty? @size == 0 end |
#eql?(other) ⇒ Boolean
Return true if ‘other` has the same type and contents as this `Vector`.
1310 1311 1312 1313 1314 |
# File 'lib/immutable/vector.rb', line 1310 def eql?(other) return true if other.equal?(self) return false unless instance_of?(other.class) && @size == other.size @root.eql?(other.instance_variable_get(:@root)) end |
#fetch(index) ⇒ Object #fetch(index) {|index| ... } ⇒ Object #fetch(index, default) ⇒ Object
Retrieve the value at ‘index` with optional default.
270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/immutable/vector.rb', line 270 def fetch(index, default = (missing_default = true)) if index >= -@size && index < @size get(index) elsif block_given? yield(index) elsif !missing_default default else raise IndexError, "index #{index} outside of vector bounds" end end |
#fill(object) ⇒ Vector #fill(object, index) ⇒ Vector #fill(object, index, length) ⇒ Vector
Replace a range of indexes with the given object.
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 |
# File 'lib/immutable/vector.rb', line 822 def fill(object, index = 0, length = nil) raise IndexError if index < -@size index += @size if index < 0 length ||= @size - index # to the end of the array, if no length given if index < @size suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, []) suffix.fill(object, 0, length) elsif index == @size suffix = Array.new(length, object) else suffix = Array.new(index - @size, nil).concat(Array.new(length, object)) index = @size end replace_suffix(index, suffix) end |
#first ⇒ Object
Return the first item in the ‘Vector`. If the vector is empty, return `nil`.
111 112 113 |
# File 'lib/immutable/vector.rb', line 111 def first get(0) end |
#flat_map ⇒ Vector
Return a new ‘Vector` with the concatenated results of running the block once for every element in this `Vector`.
531 532 533 534 535 |
# File 'lib/immutable/vector.rb', line 531 def flat_map return enum_for(:flat_map) if not block_given? return self if empty? self.class.new(super) end |
#flatten(level = -1)) ⇒ Vector
Return a new ‘Vector` with all nested vectors and arrays recursively “flattened out”. That is, their elements inserted into the new `Vector` in the place where the nested array/vector originally was. If an optional `level` argument is provided, the flattening will only be done recursively that number of times. A `level` of 0 means not to flatten at all, 1 means to only flatten nested arrays/vectors which are directly contained within this `Vector`.
610 611 612 613 614 615 616 617 618 619 620 |
# File 'lib/immutable/vector.rb', line 610 def flatten(level = -1) return self if level == 0 array = self.to_a if array.frozen? self.class.new(array.flatten(level).freeze) elsif array.flatten!(level) # returns nil if no changes were made self.class.new(array.freeze) else self end end |
#get(index) ⇒ Object Also known as: at
Retrieve the item at ‘index`. If there is none (either the provided index is too high or too low), return `nil`.
223 224 225 226 227 228 |
# File 'lib/immutable/vector.rb', line 223 def get(index) return nil if @size == 0 index += @size if index < 0 return nil if index >= @size || index < 0 leaf_node_for(@root, @levels * BITS_PER_LEVEL, index)[index & INDEX_MASK] end |
#hash ⇒ Integer
See ‘Object#hash`.
1318 1319 1320 |
# File 'lib/immutable/vector.rb', line 1318 def hash reduce(0) { |hash, item| (hash << 5) - hash + item.hash } end |
#insert(index, *items) ⇒ Vector
Return a new ‘Vector` with the given values inserted before the element at `index`. If `index` is greater than the current length, `nil` values are added to pad the `Vector` to the required size.
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/immutable/vector.rb', line 374 def insert(index, *items) raise IndexError if index < -@size index += @size if index < 0 if index < @size suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, []) suffix.unshift(*items) elsif index == @size suffix = items else suffix = Array.new(index - @size, nil).concat(items) index = @size end replace_suffix(index, suffix) end |
#last ⇒ Object
Return the last item in the ‘Vector`. If the vector is empty, return `nil`.
121 122 123 |
# File 'lib/immutable/vector.rb', line 121 def last get(-1) end |
#map ⇒ Vector, Enumerator Also known as: collect
Invoke the given block once for each item in the vector, and return a new ‘Vector` containing the values returned by the block. If no block is provided, return an enumerator.
516 517 518 519 520 |
# File 'lib/immutable/vector.rb', line 516 def map return enum_for(:map) if not block_given? return self if empty? self.class.new(super) end |
#marshal_dump ⇒ ::Array
1332 1333 1334 |
# File 'lib/immutable/vector.rb', line 1332 def marshal_dump to_a end |
#marshal_load(array) ⇒ Object
1337 1338 1339 |
# File 'lib/immutable/vector.rb', line 1337 def marshal_load(array) initialize(array.freeze) end |
#permutation(n = @size) ⇒ self, Enumerator
Yields all permutations of length ‘n` of items from the `Vector`, and then returns `self`. If no length `n` is specified, permutations of all elements will be yielded.
There is no guarantee about which order the permutations will be yielded in.
If no block is given, an ‘Enumerator` is returned instead.
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 |
# File 'lib/immutable/vector.rb', line 960 def permutation(n = @size) return enum_for(:permutation, n) if not block_given? if n < 0 || @size < n # yield nothing elsif n == 0 yield [] elsif n == 1 each { |item| yield [item] } else used, result = [], [] perms = lambda do |index| 0.upto(@size-1) do |i| if !used[i] result[index] = get(i) if index < n-1 used[i] = true perms[index+1] used[i] = false else yield result.dup end end end end perms[0] end self end |
#pop ⇒ Vector
Return a new ‘Vector` with the last element removed. Return `self` if empty.
415 416 417 418 |
# File 'lib/immutable/vector.rb', line 415 def pop return self if @size == 0 replace_suffix(@size-1, []) end |
#product(*vectors) ⇒ Vector #product ⇒ Vector
Cartesian product or multiplication.
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 |
# File 'lib/immutable/vector.rb', line 1058 def product(*vectors) # if no vectors passed, return "product" as in result of multiplying all items return super if vectors.empty? vectors.unshift(self) if vectors.any?(&:empty?) return block_given? ? self : [] end counters = Array.new(vectors.size, 0) bump_counters = lambda do i = vectors.size-1 counters[i] += 1 while counters[i] == vectors[i].size counters[i] = 0 i -= 1 return true if i == -1 # we are done counters[i] += 1 end false # not done yet end build_array = lambda do array = [] counters.each_with_index { |index,i| array << vectors[i][index] } array end if block_given? while true yield build_array[] return self if bump_counters[] end else result = [] while true result << build_array[] return result if bump_counters[] end end end |
#rassoc(obj) ⇒ Object
Assumes all elements are nested, indexable collections, and searches through them, comparing ‘obj` with the second element of each nested collection. Return the first nested collection which matches, or `nil` if none is found. Behaviour is undefined when elements do not meet assumptions (i.e. are not indexable collections).
1283 1284 1285 1286 1287 1288 1289 |
# File 'lib/immutable/vector.rb', line 1283 def rassoc(obj) each do |array| next if !array.respond_to?(:[]) return array if obj == array[1] end nil end |
#repeated_combination(n) ⇒ self, Enumerator
When invoked with a block, yields all repeated combinations of length ‘n` of items from the `Vector`, and then returns `self`. A “repeated combination” is one in which any item from the `Vector` can appear consecutively any number of times.
There is no guarantee about which order the combinations will be yielded in.
If no block is given, an ‘Enumerator` is returned instead.
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 |
# File 'lib/immutable/vector.rb', line 910 def repeated_combination(n) return enum_for(:repeated_combination, n) if not block_given? if n < 0 # yield nothing elsif n == 0 yield [] elsif n == 1 each { |item| yield [item] } elsif @size == 0 # yield nothing else combos = lambda do |result,index,remaining| while index < @size-1 if remaining == 1 yield result.dup << get(index) else combos[result.dup << get(index), index, remaining-1] end index += 1 end item = get(index) remaining.times { result << item } yield result end combos[[], 0, n] end self end |
#repeated_permutation(n = @size) ⇒ self, Enumerator
When invoked with a block, yields all repeated permutations of length ‘n` of items from the `Vector`, and then returns `self`. A “repeated permutation” is one where any item from the `Vector` can appear any number of times, and in any position (not just consecutively)
If no length ‘n` is specified, permutations of all elements will be yielded. There is no guarantee about which order the permutations will be yielded in.
If no block is given, an ‘Enumerator` is returned instead.
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 |
# File 'lib/immutable/vector.rb', line 1015 def repeated_permutation(n = @size) return enum_for(:repeated_permutation, n) if not block_given? if n < 0 # yield nothing elsif n == 0 yield [] elsif n == 1 each { |item| yield [item] } else result = [] perms = lambda do |index| 0.upto(@size-1) do |i| result[index] = get(i) if index < n-1 perms[index+1] else yield result.dup end end end perms[0] end self end |
#reverse ⇒ Vector
Return a new ‘Vector` with the same elements as this one, but in reverse order.
572 573 574 |
# File 'lib/immutable/vector.rb', line 572 def reverse self.class.new(((array = to_a).frozen? ? array.reverse : array.reverse!).freeze) end |
#reverse_each(&block) ⇒ self
Call the given block once for each item in the vector, from last to first.
474 475 476 477 478 |
# File 'lib/immutable/vector.rb', line 474 def reverse_each(&block) return enum_for(:reverse_each) unless block_given? reverse_traverse_depth_first(@root, @levels, &block) self end |
#rindex(obj) ⇒ Integer #rindex {|element| ... } ⇒ Integer
Find the index of an element, starting from the end of the vector. Returns ‘nil` if no element is found.
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 |
# File 'lib/immutable/vector.rb', line 1236 def rindex(obj = (missing_arg = true)) i = @size - 1 if missing_arg if block_given? reverse_each { |item| return i if yield item; i -= 1 } nil else enum_for(:rindex) end else reverse_each { |item| return i if item == obj; i -= 1 } nil end end |
#rotate(count = 1) ⇒ Vector
Return a new ‘Vector` with the same elements, but rotated so that the one at index `count` is the first element of the new vector. If `count` is positive, the elements will be shifted left, and those shifted past the lowest position will be moved to the end. If `count` is negative, the elements will be shifted right, and those shifted past the last position will be moved to the beginning.
589 590 591 592 |
# File 'lib/immutable/vector.rb', line 589 def rotate(count = 1) return self if (count % @size) == 0 self.class.new(((array = to_a).frozen? ? array.rotate(count) : array.rotate!(count)).freeze) end |
#sample ⇒ Object
Return a randomly chosen item from this ‘Vector`. If the vector is empty, return `nil`.
1198 1199 1200 |
# File 'lib/immutable/vector.rb', line 1198 def sample get(rand(@size)) end |
#select {|element| ... } ⇒ Vector Also known as: find_all, keep_if
Return a new ‘Vector` containing all elements for which the given block returns true.
489 490 491 492 |
# File 'lib/immutable/vector.rb', line 489 def select return enum_for(:select) unless block_given? reduce(self.class.empty) { |vector, item| yield(item) ? vector.add(item) : vector } end |
#set(index, item) ⇒ Vector #set(index) {|existing| ... } ⇒ Vector
Return a new ‘Vector` with a new value at the given `index`. If `index` is greater than the length of the vector, the returned vector will be padded with `nil`s to the correct size.
165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/immutable/vector.rb', line 165 def set(index, item = yield(get(index))) raise IndexError, "index #{index} outside of vector bounds" if index < -@size index += @size if index < 0 if index > @size suffix = Array.new(index - @size, nil) suffix << item replace_suffix(@size, suffix) else update_root(index, item) end end |
#shift ⇒ Vector
Return a new ‘Vector` with the first element removed. If empty, return `self`.
440 441 442 |
# File 'lib/immutable/vector.rb', line 440 def shift delete_at(0) end |
#shuffle ⇒ Vector
Return a new ‘Vector` with the same elements as this one, but randomly permuted.
543 544 545 |
# File 'lib/immutable/vector.rb', line 543 def shuffle self.class.new(((array = to_a).frozen? ? array.shuffle : array.shuffle!).freeze) end |
#vector.slice(index) ⇒ Object #vector.slice(index, length) ⇒ Vector #vector.slice(index..end) ⇒ Vector Also known as: []
Return specific objects from the ‘Vector`. All overloads return `nil` if the starting index is out of range.
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/immutable/vector.rb', line 340 def slice(arg, length = (missing_length = true)) if missing_length if arg.is_a?(Range) from, to = arg.begin, arg.end from += @size if from < 0 to += @size if to < 0 to += 1 if !arg.exclude_end? length = to - from length = 0 if length < 0 subsequence(from, length) else get(arg) end else arg += @size if arg < 0 subsequence(arg, length) end end |
#sort ⇒ Vector #sort {|a, b| ... } ⇒ Vector
Return a new ‘Vector` with the same items, but sorted.
692 693 694 |
# File 'lib/immutable/vector.rb', line 692 def sort self.class.new(super) end |
#sort_by {|element| ... } ⇒ Vector
Return a new ‘Vector` with the same items, but sorted. The sort order is determined by mapping the items through the given block to obtain sort keys, and then sorting the keys according to their natural sort order (`#<=>`).
708 709 710 |
# File 'lib/immutable/vector.rb', line 708 def sort_by self.class.new(super) end |
#take(n) ⇒ Vector
Return only the first ‘n` elements in a new `Vector`.
736 737 738 739 |
# File 'lib/immutable/vector.rb', line 736 def take(n) return self if n >= @size self.class.new(super) end |
#take_while ⇒ Vector, Enumerator
Gather elements up to, but not including, the first element for which the block returns ‘nil` or `false`, and return them in a new `Vector`. If no block is given, an `Enumerator` is returned instead.
764 765 766 767 |
# File 'lib/immutable/vector.rb', line 764 def take_while return enum_for(:take_while) if not block_given? self.class.new(super) end |
#to_a ⇒ Array Also known as: to_ary
Return an ‘Array` with the same elements, in the same order. The returned `Array` may or may not be frozen.
1295 1296 1297 1298 1299 1300 1301 1302 1303 |
# File 'lib/immutable/vector.rb', line 1295 def to_a if @levels == 0 # When initializing a Vector with 32 or less items, we always make # sure @root is frozen, so we can return it directly here @root else flatten_node(@root, @levels * BITS_PER_LEVEL, []) end end |
#transpose ⇒ Vector
Assume all elements are vectors or arrays and transpose the rows and columns. In other words, take the first element of each nested vector/array and gather them together into a new ‘Vector`. Do likewise for the second, third, and so on down to the end of each nested vector/array. Gather all the resulting `Vectors` into a new `Vector` and return it.
This operation is closely related to #zip. The result is almost the same as calling #zip on the first nested vector/array with the others supplied as arguments.
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 |
# File 'lib/immutable/vector.rb', line 1118 def transpose return self.class.empty if empty? result = Array.new(first.size) { [] } 0.upto(@size-1) do |i| source = get(i) if source.size != result.size raise IndexError, "element size differs (#{source.size} should be #{result.size})" end 0.upto(result.size-1) do |j| result[j].push(source[j]) end end result.map! { |a| self.class.new(a) } self.class.new(result) end |
#uniq(&block) ⇒ Vector
Return a new ‘Vector` with no duplicate elements, as determined by `#hash` and `#eql?`. For each group of equivalent elements, only the first will be retained.
555 556 557 558 559 560 561 562 563 564 |
# File 'lib/immutable/vector.rb', line 555 def uniq(&block) array = self.to_a if array.frozen? self.class.new(array.uniq(&block).freeze) elsif array.uniq!(&block) # returns nil if no changes were made self.class.new(array.freeze) else self end end |
#unshift(object) ⇒ Vector
Return a new ‘Vector` with `object` inserted before the first element, moving the other elements upwards.
429 430 431 |
# File 'lib/immutable/vector.rb', line 429 def unshift(object) insert(0, object) end |
#update_in(*key_path) {|value| ... } ⇒ Vector
Return a new ‘Vector` with a deeply nested value modified to the result of the given code block. When traversing the nested `Vector`s and `Hash`es, non-existing keys are created with empty `Hash` values.
The code block receives the existing value of the deeply nested key (or ‘nil` if it doesn’t exist). This is useful for “transforming” the value associated with a certain key.
Note that the original ‘Vector` and sub-`Vector`s and sub-`Hash`es are left unmodified; new data structure copies are created along the path wherever needed.
198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/immutable/vector.rb', line 198 def update_in(*key_path, &block) if key_path.empty? raise ArgumentError, "must have at least one key in path" end key = key_path[0] if key_path.size == 1 new_value = block.call(get(key)) else value = fetch(key, Immutable::EmptyHash) new_value = value.update_in(*key_path[1..-1], &block) end set(key, new_value) end |
#values_at(*indices) ⇒ Vector
Return a new ‘Vector` with only the elements at the given `indices`, in the order specified by `indices`. If any of the `indices` do not exist, `nil`s will appear in their places.
1212 1213 1214 |
# File 'lib/immutable/vector.rb', line 1212 def values_at(*indices) self.class.new(indices.map { |i| get(i) }.freeze) end |
#zip(*others) ⇒ Vector #zip(*others) {|pair| ... } ⇒ nil
Combine two vectors by “zipping” them together. ‘others` should be arrays and/or vectors. The corresponding elements from this `Vector` and each of `others` (that is, the elements with the same indices) will be gathered into arrays.
If ‘others` contains fewer elements than this vector, `nil` will be used for padding.
663 664 665 666 667 668 669 |
# File 'lib/immutable/vector.rb', line 663 def zip(*others) if block_given? super else self.class.new(super) end end |