erlang-terms 1.1.0 → 2.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.editorconfig +20 -0
  4. data/.gitignore +10 -18
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +15 -3
  8. data/.yardopts +6 -0
  9. data/CHANGELOG.md +9 -0
  10. data/Gemfile +21 -1
  11. data/LICENSE.txt +1 -1
  12. data/README.md +95 -17
  13. data/Rakefile +8 -3
  14. data/erlang-terms.gemspec +14 -11
  15. data/lib/erlang-terms.rb +1 -0
  16. data/lib/erlang/associable.rb +98 -0
  17. data/lib/erlang/atom.rb +257 -0
  18. data/lib/erlang/binary.rb +425 -0
  19. data/lib/erlang/bitstring.rb +464 -0
  20. data/lib/erlang/cons.rb +122 -0
  21. data/lib/erlang/enumerable.rb +160 -0
  22. data/lib/erlang/error.rb +4 -0
  23. data/lib/erlang/export.rb +110 -12
  24. data/lib/erlang/float.rb +201 -0
  25. data/lib/erlang/function.rb +259 -0
  26. data/lib/erlang/immutable.rb +101 -0
  27. data/lib/erlang/list.rb +1685 -24
  28. data/lib/erlang/map.rb +935 -21
  29. data/lib/erlang/nil.rb +73 -10
  30. data/lib/erlang/pid.rb +120 -18
  31. data/lib/erlang/port.rb +123 -0
  32. data/lib/erlang/reference.rb +161 -0
  33. data/lib/erlang/string.rb +175 -3
  34. data/lib/erlang/term.rb +24 -0
  35. data/lib/erlang/terms.rb +324 -8
  36. data/lib/erlang/terms/version.rb +1 -1
  37. data/lib/erlang/trie.rb +364 -0
  38. data/lib/erlang/tuple.rb +1582 -14
  39. data/lib/erlang/undefined.rb +32 -0
  40. metadata +49 -71
  41. data/spec/erlang/export_spec.rb +0 -17
  42. data/spec/erlang/list_spec.rb +0 -39
  43. data/spec/erlang/map_spec.rb +0 -24
  44. data/spec/erlang/nil_spec.rb +0 -18
  45. data/spec/erlang/pid_spec.rb +0 -21
  46. data/spec/erlang/string_spec.rb +0 -11
  47. data/spec/erlang/terms_spec.rb +0 -7
  48. data/spec/erlang/tuple_spec.rb +0 -20
  49. data/spec/spec_helper.rb +0 -7
@@ -0,0 +1,259 @@
1
+ module Erlang
2
+ # An `Function` is an internal function. It corresponds to the `fun F/A` and `fun(Arg1,...) -> ...` syntax from Erlang.
3
+ #
4
+ # ### Creating Functions
5
+ #
6
+ # Erlang::Function[
7
+ # arity: 0,
8
+ # uniq: "c>yRz_\xF6\xED?Hv(\x04\x19\x102",
9
+ # index: 20,
10
+ # mod: :erl_eval,
11
+ # old_index: 20,
12
+ # old_uniq: 52032458,
13
+ # pid: Erlang::Pid["nonode@nohost", 79, 0, 0],
14
+ # free_vars: Erlang::List[
15
+ # Erlang::Tuple[
16
+ # Erlang::Nil,
17
+ # :none,
18
+ # :none,
19
+ # Erlang::List[
20
+ # Erlang::Tuple[
21
+ # :clause,
22
+ # 27,
23
+ # Erlang::Nil,
24
+ # Erlang::Nil,
25
+ # Erlang::List[Erlang::Tuple[:atom, 0, :ok]]
26
+ # ]
27
+ # ]
28
+ # ]
29
+ # ]
30
+ # ]
31
+ #
32
+ class Function
33
+ include Erlang::Term
34
+ include Erlang::Immutable
35
+
36
+ # Return the arity for this `Function`
37
+ # @return [Integer]
38
+ attr_reader :arity
39
+
40
+ # Return the uniq for this `Function`
41
+ # @return [Integer]
42
+ attr_reader :uniq
43
+
44
+ # Return the index for this `Function`
45
+ # @return [Integer]
46
+ attr_reader :index
47
+
48
+ # Return the module for this `Function`
49
+ # @return [Atom]
50
+ attr_reader :mod
51
+
52
+ # Return the old index for this `Function`
53
+ # @return [Integer]
54
+ attr_reader :old_index
55
+
56
+ # Return the old uniq for this `Function`
57
+ # @return [Integer]
58
+ attr_reader :old_uniq
59
+
60
+ # Return the pid for this `Function`
61
+ # @return [Pid]
62
+ attr_reader :pid
63
+
64
+ # Return the free variables list for this `Function`
65
+ # @return [List]
66
+ attr_reader :free_vars
67
+
68
+ class << self
69
+ # Create a new `Function` populated with the given parameters.
70
+ # @param arity [Integer] The arity of the function
71
+ # @param uniq [::String, Integer] The uniq of the function
72
+ # @param index [Integer] The index of the function
73
+ # @param mod [Atom] The module atom
74
+ # @param old_index [Integer] The old index of the function
75
+ # @param old_uniq [Integer] The old uniq of the function
76
+ # @param pid [Pid] The pid of the function
77
+ # @param free_vars [List] The free variables list
78
+ # @return [Function]
79
+ # @raise [ArgumentError] if any of the parameters are of the wrong type or absent
80
+ def [](mod:, free_vars:, pid: nil, arity: nil, uniq: nil, index: nil, old_index: nil, old_uniq: nil)
81
+ return new(mod: mod, free_vars: free_vars, pid: pid, arity: arity, uniq: uniq, index: index, old_index: old_index, old_uniq: old_uniq)
82
+ end
83
+
84
+ # Compares `a` and `b` and returns whether they are less than,
85
+ # equal to, or greater than each other.
86
+ #
87
+ # @param a [Function, Export] The left argument
88
+ # @param b [Function, Export] The right argument
89
+ # @return [-1, 0, 1]
90
+ # @raise [ArgumentError] if `a` or `b` is not a `Function`
91
+ def compare(a, b)
92
+ return Erlang::Export.compare(a, b) if a.kind_of?(Erlang::Export) and b.kind_of?(Erlang::Export)
93
+ return -1 if a.kind_of?(Erlang::Function) and b.kind_of?(Erlang::Export)
94
+ return 1 if b.kind_of?(Erlang::Function) and a.kind_of?(Erlang::Export)
95
+ raise ArgumentError, "'a' must be of Erlang::Function type" unless a.kind_of?(Erlang::Function)
96
+ raise ArgumentError, "'b' must be of Erlang::Function type" unless b.kind_of?(Erlang::Function)
97
+ c = Erlang.compare(a.arity, b.arity)
98
+ return c if c != 0
99
+ c = Erlang.compare(a.uniq, b.uniq)
100
+ return c if c != 0
101
+ c = Erlang.compare(a.index, b.index)
102
+ return c if c != 0
103
+ c = Erlang.compare(a.mod, b.mod)
104
+ return c if c != 0
105
+ c = Erlang.compare(a.old_index, b.old_index)
106
+ return c if c != 0
107
+ c = Erlang.compare(a.old_uniq, b.old_uniq)
108
+ return c if c != 0
109
+ c = Erlang.compare(a.pid, b.pid)
110
+ return c if c != 0
111
+ c = Erlang.compare(a.free_vars, b.free_vars)
112
+ return c
113
+ end
114
+ end
115
+
116
+ # @private
117
+ def initialize(mod:, free_vars:, pid: nil, arity: nil, uniq: nil, index: nil, old_index: nil, old_uniq: nil)
118
+ mod = Erlang::Atom[mod]
119
+ free_vars = Erlang.from(free_vars)
120
+ raise ArgumentError, "'free_vars' must be of Erlang::List type" if not Erlang.is_list(free_vars)
121
+ pid ||= Erlang::Pid[:'node@host', 0, 0, 0]
122
+ pid = Erlang.from(pid)
123
+ raise ArgumentError, "'pid' must be of Erlang::Pid type or nil" if not Erlang.is_pid(pid)
124
+ new_function = arity.nil? ? false : true
125
+ if new_function
126
+ raise ArgumentError, 'arity must be a non-negative Integer' if not arity.is_a?(::Integer) or arity < 0
127
+ uniq ||= Digest::MD5.digest(Erlang.inspect(mod))
128
+ uniq = ensure_unsigned_integer_128(uniq)
129
+ index ||= 0
130
+ old_index ||= 0
131
+ old_uniq ||= uniq
132
+ raise ArgumentError, 'index must be a non-negative Integer' if not Erlang.is_integer(index) or index < 0
133
+ raise ArgumentError, 'old_index must be a non-negative Integer' if not Erlang.is_integer(old_index) or old_index < 0
134
+ raise ArgumentError, 'old_uniq must be a non-negative Integer' if not Erlang.is_integer(old_uniq) or old_uniq < 0
135
+ @arity = arity
136
+ @uniq = uniq
137
+ @index = index
138
+ @mod = mod
139
+ @old_index = old_index
140
+ @old_uniq = old_uniq
141
+ @pid = pid
142
+ @free_vars = free_vars
143
+ else
144
+ uniq ||= Digest::MD5.digest(Erlang.inspect(mod))
145
+ uniq = ensure_unsigned_integer_128(uniq)
146
+ index ||= 0
147
+ raise ArgumentError, 'index must be a non-negative Integer' if not Erlang.is_integer(index) or index < 0
148
+ @pid = pid
149
+ @mod = mod
150
+ @index = index
151
+ @uniq = uniq
152
+ @free_vars = free_vars
153
+ end
154
+ end
155
+
156
+ # @private
157
+ def hash
158
+ state = [@arity, @uniq, @index, @mod, @old_index, @old_uniq, @pid, @free_vars]
159
+ return state.reduce(Erlang::Function.hash) { |acc, item| (acc << 5) - acc + item.hash }
160
+ end
161
+
162
+ # Return true if this is a new function.
163
+ #
164
+ # @return [Boolean]
165
+ def new_function?
166
+ return !!(!arity.nil?)
167
+ end
168
+
169
+ # Return true if `other` has the same type and contents as this `Function`.
170
+ #
171
+ # @param other [Object] The object to compare with
172
+ # @return [Boolean]
173
+ def eql?(other)
174
+ return true if other.equal?(self)
175
+ if instance_of?(other.class)
176
+ return !!(new_function? == other.new_function? &&
177
+ arity == other.arity &&
178
+ uniq == other.uniq &&
179
+ index == other.index &&
180
+ mod == other.mod &&
181
+ old_index == other.old_index &&
182
+ old_uniq == other.old_uniq &&
183
+ pid == other.pid &&
184
+ free_vars == other.free_vars)
185
+ else
186
+ return !!(Erlang.compare(other, self) == 0)
187
+ end
188
+ end
189
+ alias :== :eql?
190
+
191
+ # Return the contents of this `Function` as a Erlang-readable `::String`.
192
+ #
193
+ # @example
194
+ # # Using the example function at the top of this page
195
+ # fun.erlang_inspect
196
+ # # => "{'function',0,<<99,62,121,82,122,95,246,237,63,72,118,40,4,25,16,50>>,20,'erl_eval',20,52032458,{'pid','nonode@nohost',79,0,0},[{[],'none','none',[{'clause',27,[],[],[{'atom',0,'ok'}]}]}]}"
197
+ #
198
+ # @return [::String]
199
+ def erlang_inspect(raw = false)
200
+ if raw == true and Erlang.respond_to?(:term_to_binary)
201
+ result = 'erlang:binary_to_term('
202
+ result << Erlang.inspect(Erlang.term_to_binary(self), raw: raw)
203
+ result << ')'
204
+ return result
205
+ else
206
+ if new_function?
207
+ return Erlang.inspect(Erlang::Tuple[:function, @arity, @uniq, @index, @mod, @old_index, @old_uniq, @pid, @free_vars], raw: raw)
208
+ else
209
+ return Erlang.inspect(Erlang::Tuple[:function, @pid, @mod, @index, @uniq, @free_vars], raw: raw)
210
+ end
211
+ end
212
+ end
213
+
214
+ # @return [String] the nicely formatted version of the `Function`
215
+ def inspect
216
+ if new_function?
217
+ return "Erlang::Function[arity: #{arity.inspect}, uniq: #{uniq.inspect}, index: #{index.inspect}, mod: #{mod.inspect}, old_index: #{old_index.inspect}, old_uniq: #{old_uniq.inspect}, pid: #{pid.inspect}, free_vars: #{free_vars.inspect}]"
218
+ else
219
+ return "Erlang::Function[pid: #{pid.inspect}, mod: #{mod.inspect}, index: #{index.inspect}, uniq: #{uniq.inspect}, free_vars: #{free_vars.inspect}]"
220
+ end
221
+ end
222
+
223
+ # @private
224
+ def pretty_print(pp)
225
+ if new_function?
226
+ pp.group(1, 'Erlang::Function[', ']') do
227
+ pp.breakable ''
228
+ pp.seplist([[:arity, arity], [:uniq, uniq], [:index, index], [:mod, mod], [:old_index, old_index], [:old_uniq, old_uniq], [:pid, pid], [:free_vars, free_vars]], nil) do |key, val|
229
+ pp.text "#{key}: "
230
+ pp.group(1) do
231
+ pp.breakable ''
232
+ val.pretty_print(pp)
233
+ end
234
+ end
235
+ end
236
+ else
237
+ pp.group(1, 'Erlang::Function[', ']') do
238
+ pp.breakable ''
239
+ pp.seplist([[:pid, pid], [:mod, mod], [:index, index], [:uniq, uniq], [:free_vars, free_vars]], nil) do |key, val|
240
+ pp.text "#{key}: "
241
+ pp.group(1) do
242
+ pp.breakable ''
243
+ val.pretty_print(pp)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ private
251
+ def ensure_unsigned_integer_128(uniq)
252
+ uniq = uniq.to_s if uniq.kind_of?(Erlang::Binary) or uniq.kind_of?(Erlang::Bitstring)
253
+ uniq = Erlang::Binary.decode_unsigned(uniq, :big) if uniq.is_a?(::String)
254
+ raise ArgumentError, "uniq must be a non-negative Integer or a 16-byte String" if not uniq.is_a?(::Integer) or uniq < 0 or uniq > 0xffffffffffffffffffffffffffffffff
255
+ return Erlang.from(uniq)
256
+ end
257
+
258
+ end
259
+ end
@@ -0,0 +1,101 @@
1
+ module Erlang
2
+ # Licensing
3
+ # =========
4
+ #
5
+ # Portions taken and modified from https://github.com/hamstergem/hamster
6
+ #
7
+ # Copyright (c) 2009-2014 Simon Harris
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # @private
29
+ module Immutable
30
+ def self.included(klass)
31
+ klass.extend(ClassMethods)
32
+ klass.instance_eval do
33
+ include InstanceMethods
34
+ end
35
+ end
36
+
37
+ # @private
38
+ module ClassMethods
39
+ def new(*args)
40
+ super.__send__(:immutable!)
41
+ end
42
+
43
+ def memoize(*names)
44
+ include MemoizeMethods unless include?(MemoizeMethods)
45
+ names.each do |name|
46
+ original_method = "__erlang_immutable_#{name}__"
47
+ alias_method original_method, name
48
+ class_eval <<-METHOD, __FILE__, __LINE__
49
+ def #{name}
50
+ if @__erlang_immutable_memory__.instance_variable_defined?(:@#{name})
51
+ @__erlang_immutable_memory__.instance_variable_get(:@#{name})
52
+ else
53
+ @__erlang_immutable_memory__.instance_variable_set(:@#{name}, #{original_method})
54
+ end
55
+ end
56
+ METHOD
57
+ end
58
+ end
59
+ end
60
+
61
+ # @private
62
+ module MemoizeMethods
63
+ def immutable!
64
+ @__erlang_immutable_memory__ = Object.new
65
+ freeze
66
+ end
67
+ end
68
+
69
+ # @private
70
+ module InstanceMethods
71
+ def immutable!
72
+ freeze
73
+ end
74
+
75
+ def immutable?
76
+ frozen?
77
+ end
78
+
79
+ alias_method :__erlang_immutable_dup__, :dup
80
+ private :__erlang_immutable_dup__
81
+
82
+ def dup
83
+ self
84
+ end
85
+
86
+ def clone
87
+ self
88
+ end
89
+
90
+ protected
91
+
92
+ def transform_unless(condition, &block)
93
+ condition ? self : transform(&block)
94
+ end
95
+
96
+ def transform(&block)
97
+ __erlang_immutable_dup__.tap { |copy| copy.instance_eval(&block) }.immutable!
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,44 +1,1705 @@
1
1
  module Erlang
2
- class List < ::Array
3
- attr_writer :tail
2
+ class ImproperListError < Erlang::Error; end
3
+ class ProperListError < Erlang::Error; end
4
4
 
5
- def improper?
6
- tail != []
5
+ # A `List` can be constructed with {List.[] List[]}.
6
+ # It consists of a *head* (the first element) and a *tail* (which itself is also
7
+ # a `List`, containing all the remaining elements).
8
+ #
9
+ # This is a singly linked list. Prepending to the list with {Erlang::List#add} runs
10
+ # in constant time. Traversing the list from front to back is efficient,
11
+ # however, indexed access runs in linear time because the list needs to be
12
+ # traversed to find the element.
13
+ #
14
+ # Licensing
15
+ # =========
16
+ #
17
+ # Portions taken and modified from https://github.com/hamstergem/hamster
18
+ #
19
+ # Copyright (c) 2009-2014 Simon Harris
20
+ #
21
+ # Permission is hereby granted, free of charge, to any person obtaining
22
+ # a copy of this software and associated documentation files (the
23
+ # "Software"), to deal in the Software without restriction, including
24
+ # without limitation the rights to use, copy, modify, merge, publish,
25
+ # distribute, sublicense, and/or sell copies of the Software, and to
26
+ # permit persons to whom the Software is furnished to do so, subject to
27
+ # the following conditions:
28
+ #
29
+ # The above copyright notice and this permission notice shall be
30
+ # included in all copies or substantial portions of the Software.
31
+ #
32
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39
+ #
40
+ module List
41
+ include Erlang::Enumerable
42
+
43
+ # @private
44
+ CADR = /^c([ad]+)r$/
45
+
46
+ # Create a new `List` populated with the given items.
47
+ #
48
+ # @example
49
+ # list = Erlang::List[:a, :b, :c]
50
+ # # => Erlang::List[:a, :b, :c]
51
+ #
52
+ # @return [List]
53
+ def self.[](*items)
54
+ return from_enum(items)
55
+ end
56
+
57
+ # Return an empty `Erlang::List`.
58
+ #
59
+ # @return [Erlang::List]
60
+ def self.empty
61
+ return Erlang::Nil
62
+ end
63
+
64
+ # This method exists distinct from `.[]` since it is ~30% faster
65
+ # than splatting the argument.
66
+ #
67
+ # Marking as private only because it was introduced for an internal
68
+ # refactoring. It could potentially be made public with a good name.
69
+ #
70
+ # @private
71
+ def self.from_enum(items)
72
+ # use destructive operations to build up a new list, like Common Lisp's NCONC
73
+ # this is a very fast way to build up a linked list
74
+ items = [items] if not items.kind_of?(::Enumerable)
75
+ out = tail = Erlang::Cons.allocate
76
+ items.each do |item|
77
+ item = Erlang.from(item)
78
+ new_node = Erlang::Cons.allocate
79
+ new_node.instance_variable_set(:@head, item)
80
+ new_node.instance_variable_set(:@improper, false)
81
+ tail.instance_variable_set(:@tail, new_node)
82
+ tail.instance_variable_set(:@improper, false)
83
+ tail.immutable!
84
+ tail = new_node
85
+ end
86
+ unless tail.immutable?
87
+ tail.instance_variable_set(:@tail, Erlang::Nil)
88
+ tail.immutable!
89
+ end
90
+ return out.tail
91
+ end
92
+
93
+ def self.compare(a, b)
94
+ return Erlang::String.compare(a, b) if a.kind_of?(Erlang::String) and b.kind_of?(Erlang::String)
95
+ a = a.to_list if a.kind_of?(Erlang::String)
96
+ b = b.to_list if b.kind_of?(Erlang::String)
97
+ raise ArgumentError, "'a' must be of Erlang::List type" if not a.kind_of?(Erlang::List)
98
+ raise ArgumentError, "'b' must be of Erlang::List type" if not b.kind_of?(Erlang::List)
99
+ c = 0
100
+ while c == 0 and a.kind_of?(Erlang::List) and b.kind_of?(Erlang::List) and not a.empty? and not b.empty?
101
+ c = Erlang.compare(a.head, b.head)
102
+ a = a.tail
103
+ b = b.tail
104
+ end
105
+ if c == 0
106
+ if not a.kind_of?(Erlang::List) or not b.kind_of?(Erlang::List)
107
+ c = Erlang.compare(a, b)
108
+ elsif a.empty? and not b.empty?
109
+ c = -1
110
+ elsif not a.empty? and b.empty?
111
+ c = 1
112
+ end
113
+ end
114
+ return c
7
115
  end
8
116
 
9
- def tail(value = nil)
10
- if value
11
- self.tail = value
12
- self
117
+ # Create a new `List` with `item` added at the front. This is a constant
118
+ # time operation.
119
+ #
120
+ # @example
121
+ # Erlang::List[:b, :c].add(:a)
122
+ # # => Erlang::List[:a, :b, :c]
123
+ #
124
+ # @param item [Object] The item to add
125
+ # @return [List]
126
+ def add(item)
127
+ return Erlang::Cons.new(item, self)
128
+ end
129
+ alias :cons :add
130
+
131
+ # Create a new `List` with `item` added at the end. This is much less efficient
132
+ # than adding items at the front.
133
+ #
134
+ # @example
135
+ # Erlang::List[:a, :b] << :c
136
+ # # => Erlang::List[:a, :b, :c]
137
+ #
138
+ # @param item [Object] The item to add
139
+ # @return [List]
140
+ def <<(item)
141
+ raise Erlang::ImproperListError if improper?
142
+ return append(Erlang::List[item])
143
+ end
144
+
145
+ # Call the given block once for each item in the list, passing each
146
+ # item from first to last successively to the block. If no block is given,
147
+ # returns an `Enumerator`.
148
+ #
149
+ # @return [self]
150
+ # @yield [item]
151
+ def each
152
+ raise Erlang::ImproperListError if improper?
153
+ return to_enum unless block_given?
154
+ list = self
155
+ until list.empty?
156
+ yield(list.head)
157
+ list = list.tail
158
+ end
159
+ return self
160
+ end
161
+
162
+ # Return a `List` in which each element is derived from the corresponding
163
+ # element in this `List`, transformed through the given block. If no block
164
+ # is given, returns an `Enumerator`.
165
+ #
166
+ # @example
167
+ # Erlang::List[3, 2, 1].map { |e| e * e } # => Erlang::List[9, 4, 1]
168
+ #
169
+ # @return [List, Enumerator]
170
+ # @yield [item]
171
+ def map(&block)
172
+ raise Erlang::ImproperListError if improper?
173
+ return enum_for(:map) unless block_given?
174
+ return self if empty?
175
+ out = tail = Erlang::Cons.allocate
176
+ list = self
177
+ until list.empty?
178
+ new_node = Erlang::Cons.allocate
179
+ new_node.instance_variable_set(:@head, Erlang.from(yield(list.head)))
180
+ new_node.instance_variable_set(:@improper, false)
181
+ tail.instance_variable_set(:@tail, new_node)
182
+ tail.instance_variable_set(:@improper, false)
183
+ tail.immutable!
184
+ tail = new_node
185
+ list = list.tail
186
+ end
187
+ if not tail.immutable?
188
+ tail.instance_variable_set(:@tail, Erlang::Nil)
189
+ tail.immutable!
190
+ end
191
+ return out.tail
192
+ end
193
+ alias :collect :map
194
+
195
+ # Return a `List` which is realized by transforming each item into a `List`,
196
+ # and flattening the resulting lists.
197
+ #
198
+ # @example
199
+ # Erlang::List[1, 2, 3].flat_map { |x| Erlang::List[x, 100] }
200
+ # # => Erlang::List[1, 100, 2, 100, 3, 100]
201
+ #
202
+ # @return [List]
203
+ def flat_map(&block)
204
+ raise Erlang::ImproperListError if improper?
205
+ return enum_for(:flat_map) unless block_given?
206
+ return self if empty?
207
+ out = tail = Erlang::Cons.allocate
208
+ list = self
209
+ until list.empty?
210
+ head_list = Erlang::List.from_enum(yield(list.head))
211
+ if head_list.empty?
212
+ list = list.tail
213
+ elsif list.tail.empty?
214
+ tail.instance_variable_set(:@head, head_list.head)
215
+ tail.instance_variable_set(:@tail, head_list.tail)
216
+ tail.immutable!
217
+ list = list.tail
218
+ else
219
+ new_node = Erlang::Cons.allocate
220
+ new_node.instance_variable_set(:@improper, false)
221
+ tail.instance_variable_set(:@head, head_list.head)
222
+ tail.instance_variable_set(:@tail, head_list.tail + new_node)
223
+ tail.immutable!
224
+ list = list.tail
225
+ tail = new_node
226
+ end
227
+ end
228
+ if not tail.immutable?
229
+ tail.instance_variable_set(:@tail, Erlang::Nil)
230
+ tail.immutable!
231
+ end
232
+ if out === tail and not out.tail.kind_of?(Erlang::List)
233
+ return out.tail
13
234
  else
14
- @tail ||= []
235
+ return out
236
+ end
237
+ end
238
+
239
+ # Return a `List` which contains all the items for which the given block
240
+ # returns true.
241
+ #
242
+ # @example
243
+ # Erlang::List["Bird", "Cow", "Elephant"].select { |e| e.size >= 4 }
244
+ # # => Erlang::List["Bird", "Elephant"]
245
+ #
246
+ # @return [List]
247
+ # @yield [item] Once for each item.
248
+ def select(&block)
249
+ raise Erlang::ImproperListError if improper?
250
+ return enum_for(:select) unless block_given?
251
+ out = tail = Erlang::Cons.allocate
252
+ list = self
253
+ while !list.empty?
254
+ if yield(list.head)
255
+ new_node = Erlang::Cons.allocate
256
+ new_node.instance_variable_set(:@head, list.head)
257
+ new_node.instance_variable_set(:@improper, false)
258
+ tail.instance_variable_set(:@tail, new_node)
259
+ tail.instance_variable_set(:@improper, false)
260
+ tail.immutable!
261
+ tail = new_node
262
+ list = list.tail
263
+ else
264
+ list = list.tail
265
+ end
266
+ end
267
+ if not tail.immutable?
268
+ tail.instance_variable_set(:@tail, Erlang::Nil)
269
+ tail.immutable!
270
+ end
271
+ return out.tail
272
+ end
273
+ alias :find_all :select
274
+ alias :keep_if :select
275
+
276
+ # Return a `List` which contains all elements up to, but not including, the
277
+ # first element for which the block returns `nil` or `false`.
278
+ #
279
+ # @example
280
+ # Erlang::List[1, 3, 5, 7, 6, 4, 2].take_while { |e| e < 5 }
281
+ # # => Erlang::List[1, 3]
282
+ #
283
+ # @return [List, Enumerator]
284
+ # @yield [item]
285
+ def take_while(&block)
286
+ raise Erlang::ImproperListError if improper?
287
+ return enum_for(:take_while) unless block_given?
288
+ return self if empty?
289
+ out = tail = Erlang::Cons.allocate
290
+ list = self
291
+ while !list.empty? && yield(list.head)
292
+ new_node = Erlang::Cons.allocate
293
+ new_node.instance_variable_set(:@head, list.head)
294
+ new_node.instance_variable_set(:@improper, false)
295
+ tail.instance_variable_set(:@tail, new_node)
296
+ tail.instance_variable_set(:@improper, false)
297
+ tail.immutable!
298
+ tail = new_node
299
+ list = list.tail
300
+ end
301
+ if not tail.immutable?
302
+ tail.instance_variable_set(:@tail, Erlang::Nil)
303
+ tail.immutable!
304
+ end
305
+ return out.tail
306
+ end
307
+
308
+ # Return a `List` which contains all elements starting from the
309
+ # first element for which the block returns `nil` or `false`.
310
+ #
311
+ # @example
312
+ # Erlang::List[1, 3, 5, 7, 6, 4, 2].drop_while { |e| e < 5 }
313
+ # # => Erlang::List[5, 7, 6, 4, 2]
314
+ #
315
+ # @return [List, Enumerator]
316
+ # @yield [item]
317
+ def drop_while(&block)
318
+ raise Erlang::ImproperListError if improper?
319
+ return enum_for(:drop_while) unless block_given?
320
+ list = self
321
+ list = list.tail while !list.empty? && yield(list.head)
322
+ return list
323
+ end
324
+
325
+ # Return a `List` containing the first `number` items from this `List`.
326
+ #
327
+ # @example
328
+ # Erlang::List[1, 3, 5, 7, 6, 4, 2].take(3)
329
+ # # => Erlang::List[1, 3, 5]
330
+ #
331
+ # @param number [Integer] The number of items to retain
332
+ # @return [List]
333
+ def take(number)
334
+ raise Erlang::ImproperListError if improper?
335
+ return self if empty?
336
+ return Erlang::Nil if number <= 0
337
+ out = tail = Erlang::Cons.allocate
338
+ list = self
339
+ while !list.empty? && number > 0
340
+ new_node = Erlang::Cons.allocate
341
+ new_node.instance_variable_set(:@head, list.head)
342
+ new_node.instance_variable_set(:@improper, false)
343
+ tail.instance_variable_set(:@tail, new_node)
344
+ tail.instance_variable_set(:@improper, false)
345
+ tail.immutable!
346
+ tail = new_node
347
+ list = list.tail
348
+ number -= 1
349
+ end
350
+ if not tail.immutable?
351
+ tail.instance_variable_set(:@tail, Erlang::Nil)
352
+ tail.immutable!
353
+ end
354
+ return out.tail
355
+ end
356
+
357
+ # Return a `List` containing all but the last item from this `List`.
358
+ #
359
+ # @example
360
+ # Erlang::List["A", "B", "C"].pop # => Erlang::List["A", "B"]
361
+ #
362
+ # @return [List]
363
+ def pop
364
+ raise Erlang::ImproperListError if improper?
365
+ return self if empty?
366
+ new_size = size - 1
367
+ return Erlang::List.new(head, tail.take(new_size - 1)) if new_size >= 1
368
+ return Erlang::Nil
369
+ end
370
+
371
+ # Return a `List` containing all items after the first `number` items from
372
+ # this `List`.
373
+ #
374
+ # @example
375
+ # Erlang::List[1, 3, 5, 7, 6, 4, 2].drop(3)
376
+ # # => Erlang::List[7, 6, 4, 2]
377
+ #
378
+ # @param number [Integer] The number of items to skip over
379
+ # @return [List]
380
+ def drop(number)
381
+ raise Erlang::ImproperListError if improper?
382
+ list = self
383
+ while !list.empty? && number > 0
384
+ number -= 1
385
+ list = list.tail
386
+ end
387
+ return list
388
+ end
389
+
390
+ # Return a `List` with all items from this `List`, followed by all items from
391
+ # `other`.
392
+ #
393
+ # @example
394
+ # Erlang::List[1, 2, 3].append(Erlang::List[4, 5])
395
+ # # => Erlang::List[1, 2, 3, 4, 5]
396
+ #
397
+ # @param other [List] The list to add onto the end of this one
398
+ # @return [List]
399
+ def append(other)
400
+ # raise Erlang::ImproperListError if improper?
401
+ other = Erlang.from(other)
402
+ return self if not improper? and Erlang.is_list(other) and other.empty?
403
+ return other if Erlang.is_list(other) and empty?
404
+ is_improper = Erlang.is_list(other) ? other.improper? : true
405
+ out = tail = Erlang::Cons.allocate
406
+ list = self
407
+ until list.empty?
408
+ new_node = Erlang::Cons.allocate
409
+ new_node.instance_variable_set(:@head, list.head)
410
+ new_node.instance_variable_set(:@improper, is_improper)
411
+ tail.instance_variable_set(:@tail, new_node)
412
+ tail.instance_variable_set(:@improper, is_improper)
413
+ tail.immutable!
414
+ tail = new_node
415
+ if not Erlang.is_list(list.tail)
416
+ new_node = Erlang::Cons.allocate
417
+ new_node.instance_variable_set(:@head, list.tail)
418
+ new_node.instance_variable_set(:@improper, is_improper)
419
+ tail.instance_variable_set(:@tail, new_node)
420
+ tail.instance_variable_set(:@improper, is_improper)
421
+ tail.immutable!
422
+ tail = new_node
423
+ list = Erlang::Nil
424
+ else
425
+ list = list.tail
426
+ end
427
+ end
428
+ if not tail.immutable?
429
+ tail.instance_variable_set(:@tail, other)
430
+ tail.immutable!
431
+ end
432
+ return out.tail
433
+ end
434
+ alias :concat :append
435
+ alias :+ :append
436
+
437
+ # Return a `List` with the same items, but in reverse order.
438
+ #
439
+ # @example
440
+ # Erlang::List["A", "B", "C"].reverse # => Erlang::List["C", "B", "A"]
441
+ #
442
+ # @return [List]
443
+ def reverse
444
+ return reduce(Erlang::Nil) { |list, item| list.cons(item) }
445
+ end
446
+
447
+ # Combine two lists by "zipping" them together. The corresponding elements
448
+ # from this `List` and each of `others` (that is, the elements with the
449
+ # same indices) will be gathered into lists.
450
+ #
451
+ # If `others` contains fewer elements than this list, `nil` will be used
452
+ # for padding.
453
+ #
454
+ # @example
455
+ # Erlang::List["A", "B", "C"].zip(Erlang::List[1, 2, 3])
456
+ # # => Erlang::List[Erlang::List["A", 1], Erlang::List["B", 2], Erlang::List["C", 3]]
457
+ #
458
+ # @param others [List] The list to zip together with this one
459
+ # @return [List]
460
+ def zip(others)
461
+ raise Erlang::ImproperListError if improper?
462
+ others = Erlang.from(others)
463
+ raise ArgumentError, "others must be of Erlang::List type" if not Erlang.is_list(others)
464
+ return self if empty? && others.empty?
465
+ out = tail = Erlang::Cons.allocate
466
+ list = self
467
+ until list.empty? or others.empty?
468
+ new_node = Erlang::Cons.allocate
469
+ new_node.instance_variable_set(:@head, Erlang::Cons.new(list.head, Erlang::Cons.new(others.head)))
470
+ new_node.instance_variable_set(:@improper, false)
471
+ tail.instance_variable_set(:@tail, new_node)
472
+ tail.instance_variable_set(:@improper, false)
473
+ tail.immutable!
474
+ tail = new_node
475
+ list = list.tail
476
+ others = others.tail
477
+ end
478
+ if not tail.immutable?
479
+ tail.instance_variable_set(:@tail, Erlang::Nil)
480
+ tail.immutable!
481
+ end
482
+ return out.tail
483
+ end
484
+
485
+ # Gather the first element of each nested list into a new `List`, then the second
486
+ # element of each nested list, then the third, and so on. In other words, if each
487
+ # nested list is a "row", return a `List` of "columns" instead.
488
+ #
489
+ # @return [List]
490
+ def transpose
491
+ raise Erlang::ImproperListError if improper?
492
+ return Erlang::Nil if empty?
493
+ return Erlang::Nil if any? { |list| list.empty? }
494
+ heads, tails = Erlang::Nil, Erlang::Nil
495
+ reverse_each { |list| heads, tails = heads.cons(list.head), tails.cons(list.tail) }
496
+ return Erlang::Cons.new(heads, tails.transpose)
497
+ end
498
+
499
+ # Return a new `List` with the same elements, but rotated so that the one at
500
+ # index `count` is the first element of the new list. If `count` is positive,
501
+ # the elements will be shifted left, and those shifted past the lowest position
502
+ # will be moved to the end. If `count` is negative, the elements will be shifted
503
+ # right, and those shifted past the last position will be moved to the beginning.
504
+ #
505
+ # @example
506
+ # l = Erlang::List["A", "B", "C", "D", "E", "F"]
507
+ # l.rotate(2) # => Erlang::List["C", "D", "E", "F", "A", "B"]
508
+ # l.rotate(-1) # => Erlang::List["F", "A", "B", "C", "D", "E"]
509
+ #
510
+ # @param count [Integer] The number of positions to shift items by
511
+ # @return [List]
512
+ # @raise [TypeError] if count is not an integer.
513
+ def rotate(count = 1)
514
+ raise Erlang::ImproperListError if improper?
515
+ raise TypeError, "expected Integer" if not count.is_a?(Integer)
516
+ return self if empty? || (count % size) == 0
517
+ count = (count >= 0) ? count % size : (size - (~count % size) - 1)
518
+ return drop(count).append(take(count))
519
+ end
520
+
521
+ # Return two `List`s, one of the first `number` items, and another with the
522
+ # remaining.
523
+ #
524
+ # @example
525
+ # Erlang::List["a", "b", "c", "d"].split_at(2)
526
+ # # => Erlang::Tuple[Erlang::List["a", "b"], Erlang::List["c", "d"]]
527
+ #
528
+ # @param number [Integer] The index at which to split this list
529
+ # @return [Tuple]
530
+ def split_at(number)
531
+ return Erlang::Tuple[take(number), drop(number)]
532
+ end
533
+
534
+ # Return two `List`s, one up to (but not including) the first item for which the
535
+ # block returns `nil` or `false`, and another of all the remaining items.
536
+ #
537
+ # @example
538
+ # Erlang::List[4, 3, 5, 2, 1].span { |x| x > 2 }
539
+ # # => Erlang::Tuple[Erlang::List[4, 3, 5], Erlang::List[2, 1]]
540
+ #
541
+ # @return [Tuple]
542
+ # @yield [item]
543
+ def span(&block)
544
+ raise Erlang::ImproperListError if improper?
545
+ return [self, EmptyList].freeze unless block_given?
546
+ left = left_tail = Erlang::Cons.allocate
547
+ list = self
548
+ while !list.empty?
549
+ if yield(list.head)
550
+ new_node = Erlang::Cons.allocate
551
+ new_node.instance_variable_set(:@head, list.head)
552
+ new_node.instance_variable_set(:@improper, false)
553
+ left_tail.instance_variable_set(:@tail, new_node)
554
+ left_tail.instance_variable_set(:@improper, false)
555
+ left_tail.immutable!
556
+ left_tail = new_node
557
+ list = list.tail
558
+ else
559
+ break
560
+ end
561
+ end
562
+ if not left_tail.immutable?
563
+ left_tail.instance_variable_set(:@tail, Erlang::Nil)
564
+ left_tail.immutable!
565
+ end
566
+ return Erlang::Tuple[left.tail, list]
567
+ end
568
+
569
+ # Return two `List`s, one up to (but not including) the first item for which the
570
+ # block returns true, and another of all the remaining items.
571
+ #
572
+ # @example
573
+ # Erlang::List[1, 3, 4, 2, 5].break { |x| x > 3 }
574
+ # # => [Erlang::List[1, 3], Erlang::List[4, 2, 5]]
575
+ #
576
+ # @return [Array]
577
+ # @yield [item]
578
+ def break(&block)
579
+ raise Erlang::ImproperListError if improper?
580
+ return span unless block_given?
581
+ return span { |item| !yield(item) }
582
+ end
583
+
584
+ # Return an empty `List`. If used on a subclass, returns an empty instance
585
+ # of that class.
586
+ #
587
+ # @return [List]
588
+ def clear
589
+ return Erlang::Nil
590
+ end
591
+
592
+ # Return a new `List` with the same items, but sorted.
593
+ #
594
+ # @overload sort
595
+ # Compare elements with their natural sort key (`#<=>`).
596
+ #
597
+ # @example
598
+ # Erlang::List["Elephant", "Dog", "Lion"].sort
599
+ # # => Erlang::List["Dog", "Elephant", "Lion"]
600
+ #
601
+ # @overload sort
602
+ # Uses the block as a comparator to determine sorted order.
603
+ #
604
+ # @yield [a, b] Any number of times with different pairs of elements.
605
+ # @yieldreturn [Integer] Negative if the first element should be sorted
606
+ # lower, positive if the latter element, or 0 if
607
+ # equal.
608
+ # @example
609
+ # Erlang::List["Elephant", "Dog", "Lion"].sort { |a,b| a.size <=> b.size }
610
+ # # => Erlang::List["Dog", "Lion", "Elephant"]
611
+ #
612
+ # @return [List]
613
+ def sort(&comparator)
614
+ comparator = Erlang.method(:compare) unless block_given?
615
+ array = super(&comparator)
616
+ return List.from_enum(array)
617
+ end
618
+
619
+ # Return a new `List` with the same items, but sorted. The sort order is
620
+ # determined by mapping the items through the given block to obtain sort
621
+ # keys, and then sorting the keys according to their natural sort order
622
+ # (`#<=>`).
623
+ #
624
+ # @yield [element] Once for each element.
625
+ # @yieldreturn a sort key object for the yielded element.
626
+ # @example
627
+ # Erlang::List["Elephant", "Dog", "Lion"].sort_by { |e| e.size }
628
+ # # => Erlang::List["Dog", "Lion", "Elephant"]
629
+ #
630
+ # @return [List]
631
+ def sort_by(&transformer)
632
+ return sort unless block_given?
633
+ block = ->(x) { Erlang.from(transformer.call(x)) }
634
+ array = super(&block)
635
+ return List.from_enum(array)
636
+ end
637
+
638
+ # Return a new `List` with `sep` inserted between each of the existing elements.
639
+ #
640
+ # @example
641
+ # Erlang::List["one", "two", "three"].intersperse(" ")
642
+ # # => Erlang::List["one", " ", "two", " ", "three"]
643
+ #
644
+ # @return [List]
645
+ def intersperse(sep)
646
+ raise Erlang::ImproperListError if improper?
647
+ return self if tail.empty?
648
+ sep = Erlang.from(sep)
649
+ out = tail = Erlang::Cons.allocate
650
+ list = self
651
+ until list.empty?
652
+ new_node = Erlang::Cons.allocate
653
+ new_node.instance_variable_set(:@head, list.head)
654
+ new_node.instance_variable_set(:@improper, false)
655
+ if not list.tail.empty?
656
+ sep_node = Erlang::Cons.allocate
657
+ sep_node.instance_variable_set(:@head, sep)
658
+ sep_node.instance_variable_set(:@improper, false)
659
+ new_node.instance_variable_set(:@tail, sep_node)
660
+ new_node.immutable!
661
+ end
662
+ tail.instance_variable_set(:@tail, new_node)
663
+ tail.instance_variable_set(:@improper, false)
664
+ tail.immutable!
665
+ if list.tail.empty?
666
+ tail = new_node
667
+ else
668
+ tail = new_node.tail
669
+ end
670
+ list = list.tail
671
+ end
672
+ if not tail.immutable?
673
+ tail.instance_variable_set(:@tail, Erlang::Nil)
674
+ tail.immutable!
675
+ end
676
+ return out.tail
677
+ end
678
+
679
+ # Return a `List` with the same items, but all duplicates removed.
680
+ # Use `#hash` and `#eql?` to determine which items are duplicates.
681
+ #
682
+ # @example
683
+ # Erlang::List[:a, :b, :a, :c, :b].uniq # => Erlang::List[:a, :b, :c]
684
+ # Erlang::List["a", "A", "b"].uniq(&:upcase) # => Erlang::List["a", "b"]
685
+ #
686
+ # @return [List]
687
+ def uniq(&block)
688
+ return _uniq(::Set.new, &block)
689
+ end
690
+
691
+ # @private
692
+ # Separate from `uniq` so as not to expose `items` in the public API.
693
+ def _uniq(items, &block)
694
+ if block_given?
695
+ out = tail = Erlang::Cons.allocate
696
+ list = self
697
+ while !list.empty?
698
+ if items.add?(block.call(list.head))
699
+ new_node = Erlang::Cons.allocate
700
+ new_node.instance_variable_set(:@head, list.head)
701
+ new_node.instance_variable_set(:@improper, false)
702
+ tail.instance_variable_set(:@tail, new_node)
703
+ tail.instance_variable_set(:@improper, false)
704
+ tail.immutable!
705
+ tail = new_node
706
+ list = list.tail
707
+ else
708
+ list = list.tail
709
+ end
710
+ end
711
+ if not tail.immutable?
712
+ tail.instance_variable_set(:@tail, Erlang::Nil)
713
+ tail.immutable!
714
+ end
715
+ return out.tail
716
+ else
717
+ out = tail = Erlang::Cons.allocate
718
+ list = self
719
+ while !list.empty?
720
+ if items.add?(list.head)
721
+ new_node = Erlang::Cons.allocate
722
+ new_node.instance_variable_set(:@head, list.head)
723
+ new_node.instance_variable_set(:@improper, false)
724
+ tail.instance_variable_set(:@tail, new_node)
725
+ tail.instance_variable_set(:@improper, false)
726
+ tail.immutable!
727
+ tail = new_node
728
+ list = list.tail
729
+ else
730
+ list = list.tail
731
+ end
732
+ end
733
+ if not tail.immutable?
734
+ tail.instance_variable_set(:@tail, Erlang::Nil)
735
+ tail.immutable!
736
+ end
737
+ return out.tail
738
+ end
739
+ end
740
+ protected :_uniq
741
+
742
+ # Return a `List` with all the elements from both this list and `other`,
743
+ # with all duplicates removed.
744
+ #
745
+ # @example
746
+ # Erlang::List[1, 2].union(Erlang::List[2, 3]) # => Erlang::List[1, 2, 3]
747
+ #
748
+ # @param other [List] The list to merge with
749
+ # @return [List]
750
+ def union(other)
751
+ raise Erlang::ImproperListError if improper?
752
+ other = Erlang.from(other)
753
+ raise ArgumentError, "other must be of Erlang::List type" if not Erlang.is_list(other)
754
+ raise Erlang::ImproperListError if other.improper?
755
+ items = ::Set.new
756
+ return _uniq(items).append(other._uniq(items))
757
+ end
758
+ alias :| :union
759
+
760
+ # Return a `List` with all elements except the last one.
761
+ #
762
+ # @example
763
+ # Erlang::List["a", "b", "c"].init # => Erlang::List["a", "b"]
764
+ #
765
+ # @return [List]
766
+ def init
767
+ raise Erlang::ImproperListError if improper?
768
+ return Erlang::Nil if tail.empty?
769
+ out = tail = Erlang::Cons.allocate
770
+ list = self
771
+ until list.tail.empty?
772
+ new_node = Erlang::Cons.allocate
773
+ new_node.instance_variable_set(:@head, list.head)
774
+ new_node.instance_variable_set(:@improper, false)
775
+ tail.instance_variable_set(:@tail, new_node)
776
+ tail.instance_variable_set(:@improper, false)
777
+ tail.immutable!
778
+ tail = new_node
779
+ list = list.tail
780
+ end
781
+ if not tail.immutable?
782
+ tail.instance_variable_set(:@tail, Erlang::Nil)
783
+ tail.immutable!
784
+ end
785
+ return out.tail
786
+ end
787
+
788
+ # Return the last item in this list.
789
+ # @return [Object]
790
+ def last(allow_improper = false)
791
+ if allow_improper and improper?
792
+ list = self
793
+ list = list.tail while list.tail.kind_of?(Erlang::List)
794
+ return list.tail
795
+ else
796
+ raise Erlang::ImproperListError if improper?
797
+ list = self
798
+ list = list.tail until list.tail.empty?
799
+ return list.head
800
+ end
801
+ end
802
+
803
+ # Return a `List` of all suffixes of this list.
804
+ #
805
+ # @example
806
+ # Erlang::List[1,2,3].tails
807
+ # # => Erlang::List[
808
+ # # Erlang::List[1, 2, 3],
809
+ # # Erlang::List[2, 3],
810
+ # # Erlang::List[3]]
811
+ #
812
+ # @return [List]
813
+ def tails
814
+ raise Erlang::ImproperListError if improper?
815
+ return self if empty?
816
+ out = tail = Erlang::Cons.allocate
817
+ list = self
818
+ until list.empty?
819
+ new_node = Erlang::Cons.allocate
820
+ new_node.instance_variable_set(:@head, list)
821
+ new_node.instance_variable_set(:@improper, false)
822
+ tail.instance_variable_set(:@tail, new_node)
823
+ list = list.tail
824
+ tail = new_node
15
825
  end
826
+ tail.instance_variable_set(:@tail, Erlang::Nil)
827
+ return out.tail
16
828
  end
17
829
 
830
+ # Return a `List` of all prefixes of this list.
831
+ #
832
+ # @example
833
+ # Erlang::List[1,2,3].inits
834
+ # # => Erlang::List[
835
+ # # Erlang::List[1],
836
+ # # Erlang::List[1, 2],
837
+ # # Erlang::List[1, 2, 3]]
838
+ #
839
+ # @return [List]
840
+ def inits
841
+ raise Erlang::ImproperListError if improper?
842
+ return self if empty?
843
+ prev = nil
844
+ return map do |head|
845
+ if prev.nil?
846
+ Erlang::List.from_enum(prev = [head])
847
+ else
848
+ Erlang::List.from_enum(prev.push(head))
849
+ end
850
+ end
851
+ end
852
+
853
+ # Return a `List` of all combinations of length `n` of items from this `List`.
854
+ #
855
+ # @example
856
+ # Erlang::List[1,2,3].combination(2)
857
+ # # => Erlang::List[
858
+ # # Erlang::List[1, 2],
859
+ # # Erlang::List[1, 3],
860
+ # # Erlang::List[2, 3]]
861
+ #
862
+ # @return [List]
863
+ def combination(n)
864
+ raise Erlang::ImproperListError if improper?
865
+ return Erlang::Cons.new(Erlang::Nil) if n == 0
866
+ return self if empty?
867
+ return tail.combination(n - 1).map { |list| list.cons(head) }.append(tail.combination(n))
868
+ end
869
+
870
+ # Split the items in this list in groups of `number`. Return a list of lists.
871
+ #
872
+ # @example
873
+ # ("a".."o").to_list.chunk(5)
874
+ # # => Erlang::List[
875
+ # # Erlang::List["a", "b", "c", "d", "e"],
876
+ # # Erlang::List["f", "g", "h", "i", "j"],
877
+ # # Erlang::List["k", "l", "m", "n", "o"]]
878
+ #
879
+ # @return [List]
880
+ def chunk(number)
881
+ raise Erlang::ImproperListError if improper?
882
+ return self if empty?
883
+ out = tail = Erlang::Cons.allocate
884
+ list = self
885
+ until list.empty?
886
+ first, list = list.split_at(number)
887
+ new_node = Erlang::Cons.allocate
888
+ new_node.instance_variable_set(:@head, first)
889
+ new_node.instance_variable_set(:@improper, false)
890
+ tail.instance_variable_set(:@tail, new_node)
891
+ tail.instance_variable_set(:@improper, false)
892
+ tail.immutable!
893
+ tail = new_node
894
+ end
895
+ if not tail.immutable?
896
+ tail.instance_variable_set(:@tail, Erlang::Nil)
897
+ tail.immutable!
898
+ end
899
+ return out.tail
900
+ end
901
+
902
+ # Split the items in this list in groups of `number`, and yield each group
903
+ # to the block (as a `List`). If no block is given, returns an
904
+ # `Enumerator`.
905
+ #
906
+ # @return [self, Enumerator]
907
+ # @yield [list] Once for each chunk.
908
+ def each_chunk(number, &block)
909
+ raise Erlang::ImproperListError if improper?
910
+ return enum_for(:each_chunk, number) unless block_given?
911
+ chunk(number).each(&block)
912
+ return self
913
+ end
914
+ alias :each_slice :each_chunk
915
+
916
+ # Return a new `List` with all nested lists recursively "flattened out",
917
+ # that is, their elements inserted into the new `List` in the place where
918
+ # the nested list originally was.
919
+ #
920
+ # @example
921
+ # Erlang::List[Erlang::List[1, 2], Erlang::List[3, 4]].flatten
922
+ # # => Erlang::List[1, 2, 3, 4]
923
+ #
924
+ # @return [List]
925
+ def flatten
926
+ raise Erlang::ImproperListError if improper?
927
+ return self if empty?
928
+ out = tail = Erlang::Cons.allocate
929
+ list = self
930
+ until list.empty?
931
+ if list.head.is_a?(Erlang::Cons)
932
+ list = list.head.append(list.tail)
933
+ elsif Erlang::Nil.equal?(list.head)
934
+ list = list.tail
935
+ else
936
+ new_node = Erlang::Cons.allocate
937
+ new_node.instance_variable_set(:@head, list.head)
938
+ new_node.instance_variable_set(:@improper, false)
939
+ tail.instance_variable_set(:@tail, new_node)
940
+ tail.immutable!
941
+ list = list.tail
942
+ tail = new_node
943
+ end
944
+ end
945
+ if not tail.immutable?
946
+ tail.instance_variable_set(:@tail, Erlang::Nil)
947
+ tail.immutable!
948
+ end
949
+ return out.tail
950
+ end
951
+
952
+ # Passes each item to the block, and gathers them into a {Map} where the
953
+ # keys are return values from the block, and the values are `List`s of items
954
+ # for which the block returned that value.
955
+ #
956
+ # @return [Map]
957
+ # @yield [item]
958
+ # @example
959
+ # Erlang::List["a", "b", "ab"].group_by { |e| e.size }
960
+ # # Erlang::Map[
961
+ # # 1 => Erlang::List["b", "a"],
962
+ # # 2 => Erlang::List["ab"]
963
+ # # ]
964
+ def group_by(&block)
965
+ return group_by_with(Erlang::Nil, &block)
966
+ end
967
+ alias :group :group_by
968
+
969
+ # Retrieve the item at `index`. Negative indices count back from the end of
970
+ # the list (-1 is the last item). If `index` is invalid (either too high or
971
+ # too low), return `nil`.
972
+ #
973
+ # @param index [Integer] The index to retrieve
974
+ # @return [Object]
975
+ def at(index)
976
+ raise Erlang::ImproperListError if improper?
977
+ index += size if index < 0
978
+ return nil if index < 0
979
+ node = self
980
+ while index > 0
981
+ node = node.tail
982
+ index -= 1
983
+ end
984
+ return node.head
985
+ end
986
+
987
+ # Return specific objects from the `List`. All overloads return `nil` if
988
+ # the starting index is out of range.
989
+ #
990
+ # @overload list.slice(index)
991
+ # Returns a single object at the given `index`. If `index` is negative,
992
+ # count backwards from the end.
993
+ #
994
+ # @param index [Integer] The index to retrieve. May be negative.
995
+ # @return [Object]
996
+ # @example
997
+ # l = Erlang::List["A", "B", "C", "D", "E", "F"]
998
+ # l[2] # => "C"
999
+ # l[-1] # => "F"
1000
+ # l[6] # => nil
1001
+ #
1002
+ # @overload list.slice(index, length)
1003
+ # Return a sublist starting at `index` and continuing for `length`
1004
+ # elements or until the end of the `List`, whichever occurs first.
1005
+ #
1006
+ # @param start [Integer] The index to start retrieving items from. May be
1007
+ # negative.
1008
+ # @param length [Integer] The number of items to retrieve.
1009
+ # @return [List]
1010
+ # @example
1011
+ # l = Erlang::List["A", "B", "C", "D", "E", "F"]
1012
+ # l[2, 3] # => Erlang::List["C", "D", "E"]
1013
+ # l[-2, 3] # => Erlang::List["E", "F"]
1014
+ # l[20, 1] # => nil
1015
+ #
1016
+ # @overload list.slice(index..end)
1017
+ # Return a sublist starting at `index` and continuing to index
1018
+ # `end` or the end of the `List`, whichever occurs first.
1019
+ #
1020
+ # @param range [Range] The range of indices to retrieve.
1021
+ # @return [Vector]
1022
+ # @example
1023
+ # l = Erlang::List["A", "B", "C", "D", "E", "F"]
1024
+ # l[2..3] # => Erlang::List["C", "D"]
1025
+ # l[-2..100] # => Erlang::List["E", "F"]
1026
+ # l[20..21] # => nil
1027
+ def slice(arg, length = (missing_length = true))
1028
+ raise Erlang::ImproperListError if improper?
1029
+ if missing_length
1030
+ if arg.is_a?(Range)
1031
+ from, to = arg.begin, arg.end
1032
+ from += size if from < 0
1033
+ return nil if from < 0
1034
+ to += size if to < 0
1035
+ to += 1 if !arg.exclude_end?
1036
+ length = to - from
1037
+ length = 0 if length < 0
1038
+ list = self
1039
+ while from > 0
1040
+ return nil if list.empty?
1041
+ list = list.tail
1042
+ from -= 1
1043
+ end
1044
+ return list.take(length)
1045
+ else
1046
+ return at(arg)
1047
+ end
1048
+ else
1049
+ return nil if length < 0
1050
+ arg += size if arg < 0
1051
+ return nil if arg < 0
1052
+ list = self
1053
+ while arg > 0
1054
+ return nil if list.empty?
1055
+ list = list.tail
1056
+ arg -= 1
1057
+ end
1058
+ return list.take(length)
1059
+ end
1060
+ end
1061
+ alias :[] :slice
1062
+
1063
+ # Return a `List` of indices of matching objects.
1064
+ #
1065
+ # @overload indices(object)
1066
+ # Return a `List` of indices where `object` is found. Use `#==` for
1067
+ # testing equality.
1068
+ #
1069
+ # @example
1070
+ # Erlang::List[1, 2, 3, 4].indices(2)
1071
+ # # => Erlang::List[1]
1072
+ #
1073
+ # @overload indices
1074
+ # Pass each item successively to the block. Return a list of indices
1075
+ # where the block returns true.
1076
+ #
1077
+ # @yield [item]
1078
+ # @example
1079
+ # Erlang::List[1, 2, 3, 4].indices { |e| e.even? }
1080
+ # # => Erlang::List[1, 3]
1081
+ #
1082
+ # @return [List]
1083
+ def indices(object = Erlang::Undefined, i = 0, &block)
1084
+ raise Erlang::ImproperListError if improper?
1085
+ object = Erlang.from(object) if object != Erlang::Undefined
1086
+ return indices { |item| item == object } if not block_given?
1087
+ return Erlang::Nil if empty?
1088
+ out = tail = Erlang::Cons.allocate
1089
+ list = self
1090
+ until list.empty?
1091
+ if yield(list.head)
1092
+ new_node = Erlang::Cons.allocate
1093
+ new_node.instance_variable_set(:@head, i)
1094
+ new_node.instance_variable_set(:@improper, false)
1095
+ tail.instance_variable_set(:@tail, new_node)
1096
+ tail.instance_variable_set(:@improper, false)
1097
+ tail.immutable!
1098
+ tail = new_node
1099
+ list = list.tail
1100
+ else
1101
+ list = list.tail
1102
+ end
1103
+ i += 1
1104
+ end
1105
+ if not tail.immutable?
1106
+ tail.instance_variable_set(:@tail, Erlang::Nil)
1107
+ tail.immutable!
1108
+ end
1109
+ return out.tail
1110
+ end
1111
+
1112
+ # Merge all the nested lists into a single list, using the given comparator
1113
+ # block to determine the order which items should be shifted out of the nested
1114
+ # lists and into the output list.
1115
+ #
1116
+ # @example
1117
+ # list_1 = Erlang::List[1, -3, -5]
1118
+ # list_2 = Erlang::List[-2, 4, 6]
1119
+ # Erlang::List[list_1, list_2].merge { |a,b| a.abs <=> b.abs }
1120
+ # # => Erlang::List[1, -2, -3, 4, -5, 6]
1121
+ #
1122
+ # @return [List]
1123
+ # @yield [a, b] Pairs of items from matching indices in each list.
1124
+ # @yieldreturn [Integer] Negative if the first element should be selected
1125
+ # first, positive if the latter element, or zero if
1126
+ # either.
1127
+ def merge(&comparator)
1128
+ raise Erlang::ImproperListError if improper?
1129
+ return merge_by unless block_given?
1130
+ sorted = reject(&:empty?).sort do |a, b|
1131
+ yield(a.head, b.head)
1132
+ end
1133
+ return Erlang::Nil if sorted.empty?
1134
+ return Erlang::Cons.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge(&comparator))
1135
+ end
1136
+
1137
+ # Merge all the nested lists into a single list, using sort keys generated
1138
+ # by mapping the items in the nested lists through the given block to determine the
1139
+ # order which items should be shifted out of the nested lists and into the output
1140
+ # list. Whichever nested list's `#head` has the "lowest" sort key (according to
1141
+ # their natural order) will be the first in the merged `List`.
1142
+ #
1143
+ # @example
1144
+ # list_1 = Erlang::List[1, -3, -5]
1145
+ # list_2 = Erlang::List[-2, 4, 6]
1146
+ # Erlang::List[list_1, list_2].merge_by { |x| x.abs }
1147
+ # # => Erlang::List[1, -2, -3, 4, -5, 6]
1148
+ #
1149
+ # @return [List]
1150
+ # @yield [item] Once for each item in either list.
1151
+ # @yieldreturn [Object] A sort key for the element.
1152
+ def merge_by(&transformer)
1153
+ raise Erlang::ImproperListError if improper?
1154
+ return merge_by { |item| item } unless block_given?
1155
+ sorted = reject(&:empty?).sort_by do |list|
1156
+ yield(list.head)
1157
+ end
1158
+ return Erlang::Nil if sorted.empty?
1159
+ return Erlang::Cons.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge_by(&transformer))
1160
+ end
1161
+
1162
+ # Return a randomly chosen element from this list.
1163
+ # @return [Object]
1164
+ def sample
1165
+ return at(rand(size))
1166
+ end
1167
+
1168
+ # Return a new `List` with the given items inserted before the item at `index`.
1169
+ #
1170
+ # @example
1171
+ # Erlang::List["A", "D", "E"].insert(1, "B", "C") # => Erlang::List["A", "B", "C", "D", "E"]
1172
+ #
1173
+ # @param index [Integer] The index where the new items should go
1174
+ # @param items [Array] The items to add
1175
+ # @return [List]
1176
+ def insert(index, *items)
1177
+ raise Erlang::ImproperListError if improper?
1178
+ if index == 0
1179
+ return Erlang::List.from_enum(items).append(self)
1180
+ elsif index > 0
1181
+ out = tail = Erlang::Cons.allocate
1182
+ list = self
1183
+ while index > 0
1184
+ new_node = Erlang::Cons.allocate
1185
+ new_node.instance_variable_set(:@head, list.head)
1186
+ new_node.instance_variable_set(:@improper, false)
1187
+ tail.instance_variable_set(:@tail, new_node)
1188
+ tail.instance_variable_set(:@improper, false)
1189
+ tail.immutable!
1190
+ tail = new_node
1191
+ list = list.tail
1192
+ index -= 1
1193
+ end
1194
+ if not tail.immutable?
1195
+ tail.instance_variable_set(:@tail, Erlang::List.from_enum(items).append(list))
1196
+ tail.immutable!
1197
+ end
1198
+ return out.tail
1199
+ else
1200
+ raise IndexError if index < -size
1201
+ return insert(index + size, *items)
1202
+ end
1203
+ end
1204
+
1205
+ # Return a `List` with all elements equal to `obj` removed. `#==` is used
1206
+ # for testing equality.
1207
+ #
1208
+ # @example
1209
+ # Erlang::List[:a, :b, :a, :a, :c].delete(:a) # => Erlang::List[:b, :c]
1210
+ #
1211
+ # @param obj [Object] The object to remove.
1212
+ # @return [List]
1213
+ def delete(obj)
1214
+ raise Erlang::ImproperListError if improper?
1215
+ obj = Erlang.from(obj)
1216
+ list = self
1217
+ list = list.tail while list.head == obj && !list.empty?
1218
+ return Erlang::Nil if list.empty?
1219
+ out = tail = Erlang::Cons.allocate
1220
+ until list.empty?
1221
+ if list.head == obj
1222
+ list = list.tail
1223
+ else
1224
+ new_node = Erlang::Cons.allocate
1225
+ new_node.instance_variable_set(:@head, list.head)
1226
+ new_node.instance_variable_set(:@improper, false)
1227
+ tail.instance_variable_set(:@tail, new_node)
1228
+ tail.instance_variable_set(:@improper, false)
1229
+ tail.immutable!
1230
+ tail = new_node
1231
+ list = list.tail
1232
+ end
1233
+ end
1234
+ if not tail.immutable?
1235
+ tail.instance_variable_set(:@tail, Erlang::Nil)
1236
+ tail.immutable!
1237
+ end
1238
+ return out.tail
1239
+ end
1240
+
1241
+ # Return a `List` containing the same items, minus the one at `index`.
1242
+ # If `index` is negative, it counts back from the end of the list.
1243
+ #
1244
+ # @example
1245
+ # Erlang::List[1, 2, 3].delete_at(1) # => Erlang::List[1, 3]
1246
+ # Erlang::List[1, 2, 3].delete_at(-1) # => Erlang::List[1, 2]
1247
+ #
1248
+ # @param index [Integer] The index of the item to remove
1249
+ # @return [List]
1250
+ def delete_at(index)
1251
+ raise Erlang::ImproperListError if improper?
1252
+ if index == 0
1253
+ tail
1254
+ elsif index < 0
1255
+ index += size if index < 0
1256
+ return self if index < 0
1257
+ delete_at(index)
1258
+ else
1259
+ out = tail = Erlang::Cons.allocate
1260
+ list = self
1261
+ while index > 0
1262
+ new_node = Erlang::Cons.allocate
1263
+ new_node.instance_variable_set(:@head, list.head)
1264
+ new_node.instance_variable_set(:@improper, false)
1265
+ tail.instance_variable_set(:@tail, new_node)
1266
+ tail.instance_variable_set(:@improper, false)
1267
+ tail.immutable!
1268
+ tail = new_node
1269
+ list = list.tail
1270
+ index -= 1
1271
+ end
1272
+ if not tail.immutable?
1273
+ tail.instance_variable_set(:@tail, list.tail)
1274
+ tail.immutable!
1275
+ end
1276
+ return out.tail
1277
+ end
1278
+ end
1279
+
1280
+ # Replace a range of indexes with the given object.
1281
+ #
1282
+ # @overload fill(object)
1283
+ # Return a new `List` of the same size, with every index set to `object`.
1284
+ #
1285
+ # @param [Object] object Fill value.
1286
+ # @example
1287
+ # Erlang::List["A", "B", "C", "D", "E", "F"].fill("Z")
1288
+ # # => Erlang::List["Z", "Z", "Z", "Z", "Z", "Z"]
1289
+ #
1290
+ # @overload fill(object, index)
1291
+ # Return a new `List` with all indexes from `index` to the end of the
1292
+ # vector set to `obj`.
1293
+ #
1294
+ # @param [Object] object Fill value.
1295
+ # @param [Integer] index Starting index. May be negative.
1296
+ # @example
1297
+ # Erlang::List["A", "B", "C", "D", "E", "F"].fill("Z", 3)
1298
+ # # => Erlang::List["A", "B", "C", "Z", "Z", "Z"]
1299
+ #
1300
+ # @overload fill(object, index, length)
1301
+ # Return a new `List` with `length` indexes, beginning from `index`,
1302
+ # set to `obj`. Expands the `List` if `length` would extend beyond the
1303
+ # current length.
1304
+ #
1305
+ # @param [Object] object Fill value.
1306
+ # @param [Integer] index Starting index. May be negative.
1307
+ # @param [Integer] length
1308
+ # @example
1309
+ # Erlang::List["A", "B", "C", "D", "E", "F"].fill("Z", 3, 2)
1310
+ # # => Erlang::List["A", "B", "C", "Z", "Z", "F"]
1311
+ # Erlang::List["A", "B"].fill("Z", 1, 5)
1312
+ # # => Erlang::List["A", "Z", "Z", "Z", "Z", "Z"]
1313
+ #
1314
+ # @return [List]
1315
+ # @raise [IndexError] if index is out of negative range.
1316
+ def fill(obj, index = 0, length = nil)
1317
+ raise Erlang::ImproperListError if improper?
1318
+ obj = Erlang.from(obj)
1319
+ if index == 0
1320
+ length ||= size
1321
+ if length > 0
1322
+ out = tail = Erlang::Cons.allocate
1323
+ list = self
1324
+ while length > 0
1325
+ new_node = Erlang::Cons.allocate
1326
+ new_node.instance_variable_set(:@head, obj)
1327
+ new_node.instance_variable_set(:@improper, false)
1328
+ tail.instance_variable_set(:@tail, new_node)
1329
+ tail.instance_variable_set(:@improper, false)
1330
+ tail.immutable!
1331
+ tail = new_node
1332
+ list = list.tail
1333
+ length -= 1
1334
+ end
1335
+ if not tail.immutable?
1336
+ tail.instance_variable_set(:@tail, list)
1337
+ tail.immutable!
1338
+ end
1339
+ return out.tail
1340
+ else
1341
+ self
1342
+ end
1343
+ elsif index > 0
1344
+ length ||= size - index
1345
+ length = 0 if length < 0
1346
+ out = tail = Erlang::Cons.allocate
1347
+ list = self
1348
+ while index > 0
1349
+ new_node = Erlang::Cons.allocate
1350
+ new_node.instance_variable_set(:@head, list.head)
1351
+ new_node.instance_variable_set(:@improper, false)
1352
+ tail.instance_variable_set(:@tail, new_node)
1353
+ tail.instance_variable_set(:@improper, false)
1354
+ tail.immutable!
1355
+ tail = new_node
1356
+ list = list.tail
1357
+ index -= 1
1358
+ end
1359
+ while length > 0
1360
+ new_node = Erlang::Cons.allocate
1361
+ new_node.instance_variable_set(:@head, obj)
1362
+ new_node.instance_variable_set(:@improper, false)
1363
+ tail.instance_variable_set(:@tail, new_node)
1364
+ tail.instance_variable_set(:@improper, false)
1365
+ tail.immutable!
1366
+ tail = new_node
1367
+ list = list.tail
1368
+ length -= 1
1369
+ end
1370
+ if not tail.immutable?
1371
+ tail.instance_variable_set(:@tail, list)
1372
+ tail.immutable!
1373
+ end
1374
+ return out.tail
1375
+ else
1376
+ raise IndexError if index < -size
1377
+ return fill(obj, index + size, length)
1378
+ end
1379
+ end
1380
+
1381
+ # Yields all permutations of length `n` of the items in the list, and then
1382
+ # returns `self`. If no length `n` is specified, permutations of the entire
1383
+ # list will be yielded.
1384
+ #
1385
+ # There is no guarantee about which order the permutations will be yielded in.
1386
+ #
1387
+ # If no block is given, an `Enumerator` is returned instead.
1388
+ #
1389
+ # @example
1390
+ # Erlang::List[1, 2, 3].permutation.to_a
1391
+ # # => [Erlang::List[1, 2, 3],
1392
+ # # Erlang::List[2, 1, 3],
1393
+ # # Erlang::List[2, 3, 1],
1394
+ # # Erlang::List[1, 3, 2],
1395
+ # # Erlang::List[3, 1, 2],
1396
+ # # Erlang::List[3, 2, 1]]
1397
+ #
1398
+ # @return [self, Enumerator]
1399
+ # @yield [list] Once for each permutation.
1400
+ def permutation(length = size, &block)
1401
+ raise Erlang::ImproperListError if improper?
1402
+ return enum_for(:permutation, length) if not block_given?
1403
+ if length == 0
1404
+ yield Erlang::Nil
1405
+ elsif length == 1
1406
+ each { |obj| yield Erlang::Cons.new(obj, Erlang::Nil) }
1407
+ elsif not empty?
1408
+ if length < size
1409
+ tail.permutation(length, &block)
1410
+ end
1411
+
1412
+ tail.permutation(length-1) do |p|
1413
+ 0.upto(length-1) do |i|
1414
+ left,right = p.split_at(i)
1415
+ yield left.append(right.cons(head))
1416
+ end
1417
+ end
1418
+ end
1419
+ return self
1420
+ end
1421
+
1422
+ # Yield every non-empty sublist to the given block. (The entire `List` also
1423
+ # counts as one sublist.)
1424
+ #
1425
+ # @example
1426
+ # Erlang::List[1, 2, 3].subsequences { |list| p list }
1427
+ # # prints:
1428
+ # # Erlang::List[1]
1429
+ # # Erlang::List[1, 2]
1430
+ # # Erlang::List[1, 2, 3]
1431
+ # # Erlang::List[2]
1432
+ # # Erlang::List[2, 3]
1433
+ # # Erlang::List[3]
1434
+ #
1435
+ # @yield [sublist] One or more contiguous elements from this list
1436
+ # @return [self]
1437
+ def subsequences(&block)
1438
+ raise Erlang::ImproperListError if improper?
1439
+ return enum_for(:subsequences) if not block_given?
1440
+ if not empty?
1441
+ 1.upto(size) do |n|
1442
+ yield take(n)
1443
+ end
1444
+ tail.subsequences(&block)
1445
+ end
1446
+ return self
1447
+ end
1448
+
1449
+ # Return two `List`s, the first containing all the elements for which the
1450
+ # block evaluates to true, the second containing the rest.
1451
+ #
1452
+ # @example
1453
+ # Erlang::List[1, 2, 3, 4, 5, 6].partition { |x| x.even? }
1454
+ # # => Erlang::Tuple[Erlang::List[2, 4, 6], Erlang::List[1, 3, 5]]
1455
+ #
1456
+ # @return [Tuple]
1457
+ # @yield [item] Once for each item.
1458
+ def partition(&block)
1459
+ raise Erlang::ImproperListError if improper?
1460
+ return enum_for(:partition) if not block_given?
1461
+ left = left_tail = Erlang::Cons.allocate
1462
+ right = right_tail = Erlang::Cons.allocate
1463
+ list = self
1464
+ while !list.empty?
1465
+ if yield(list.head)
1466
+ new_node = Erlang::Cons.allocate
1467
+ new_node.instance_variable_set(:@head, list.head)
1468
+ new_node.instance_variable_set(:@improper, false)
1469
+ left_tail.instance_variable_set(:@tail, new_node)
1470
+ left_tail.instance_variable_set(:@improper, false)
1471
+ left_tail.immutable!
1472
+ left_tail = new_node
1473
+ list = list.tail
1474
+ else
1475
+ new_node = Erlang::Cons.allocate
1476
+ new_node.instance_variable_set(:@head, list.head)
1477
+ new_node.instance_variable_set(:@improper, false)
1478
+ right_tail.instance_variable_set(:@tail, new_node)
1479
+ right_tail.instance_variable_set(:@improper, false)
1480
+ right_tail.immutable!
1481
+ right_tail = new_node
1482
+ list = list.tail
1483
+ end
1484
+ end
1485
+ if not left_tail.immutable?
1486
+ left_tail.instance_variable_set(:@tail, Erlang::Nil)
1487
+ left_tail.immutable!
1488
+ end
1489
+ if not right_tail.immutable?
1490
+ right_tail.instance_variable_set(:@tail, Erlang::Nil)
1491
+ right_tail.immutable!
1492
+ end
1493
+ return Erlang::Tuple[left.tail, right.tail]
1494
+ end
1495
+
1496
+ # Return true if `other` has the same type and contents as this `Hash`.
1497
+ #
1498
+ # @param other [Object] The collection to compare with
1499
+ # @return [Boolean]
1500
+ def eql?(other)
1501
+ return true if other.equal?(self)
1502
+ if other.kind_of?(Erlang::List)
1503
+ return false if other.kind_of?(Erlang::List) and improper? != other.improper?
1504
+ return false if not other.kind_of?(Erlang::List) and improper?
1505
+ other = Erlang::List.from_enum(other) if other.is_a?(::Array)
1506
+ return false if not other.kind_of?(Erlang::List)
1507
+ list = self
1508
+ while Erlang.is_list(list) and Erlang.is_list(other) and !list.empty? and !other.empty?
1509
+ return true if other.equal?(list)
1510
+ return other.empty? if list.empty?
1511
+ return false if other.empty?
1512
+ return false if not other.head.eql?(list.head)
1513
+ list = list.tail
1514
+ other = other.tail
1515
+ end
1516
+ return true if other.equal?(list)
1517
+ return list.eql?(other) if not Erlang.is_list(list) and not Erlang.is_list(other)
1518
+ return false if not Erlang.is_list(list) or not Erlang.is_list(other)
1519
+ return other.empty? if list.empty?
1520
+ return false
1521
+ else
1522
+ return !!(Erlang.compare(other, self) == 0)
1523
+ end
1524
+ end
1525
+ alias :== :eql?
1526
+
1527
+ # See `Object#hash`
1528
+ # @return [Integer]
1529
+ def hash
1530
+ if improper?
1531
+ hash = to_proper_list.hash
1532
+ return (hash << 5) - hash + last(true).hash
1533
+ else
1534
+ hash = reduce(0) { |acc, item| (acc << 5) - acc + item.hash }
1535
+ return (hash << 5) - hash + Erlang::Nil.hash
1536
+ end
1537
+ end
1538
+
1539
+ # def hash
1540
+ # hashed = improper? ? proper.hash : reduce(0) { |hash, item| (hash << 5) - hash + item.hash }
1541
+ # if improper?
1542
+ # hashed = (hashed << 5) - hashed + improper_last.hash
1543
+ # end
1544
+ # return Erlang::List.hash ^ hashed
1545
+ # end
1546
+
1547
+ # Return `self`. Since this is an immutable object duplicates are
1548
+ # equivalent.
1549
+ # @return [List]
1550
+ def dup
1551
+ return self
1552
+ end
1553
+ alias :clone :dup
1554
+
1555
+ # def to_atom
1556
+ # return Erlang::Atom[self]
1557
+ # end
1558
+
1559
+ # def to_binary
1560
+ # return Erlang::Binary[self]
1561
+ # end
1562
+
1563
+ # def to_list
1564
+ # return self
1565
+ # end
1566
+
1567
+ # def to_map
1568
+ # return Erlang::Map.from_list(self)
1569
+ # end
1570
+
1571
+ # def to_string
1572
+ # return Erlang::String[self]
1573
+ # end
1574
+
1575
+ # def to_tuple
1576
+ # return Erlang::Tuple.from_enum(self)
1577
+ # end
1578
+
1579
+ # def respond_to?(name, include_private = false)
1580
+ # super || !!name.to_s.match(CADR)
1581
+ # end
1582
+
1583
+ # Return `List` with proper ending elemnet `Erlang::Nil`.
1584
+ # If the list is already proper, `self` is returned.
1585
+ # @return [List]
1586
+ def to_proper_list
1587
+ return self if not improper?
1588
+ return (self + Erlang::Nil).init
1589
+ end
1590
+
1591
+ # def |(value)
1592
+ # value = value.__erlang_term__ if not value.kind_of?(Erlang::Term)
1593
+ # return value if empty?
1594
+ # improper = Erlang::List.is_improper?(value)
1595
+ # out = tail = Erlang::List.allocate
1596
+ # list = self
1597
+ # while Erlang::List.is_list?(list) && !list.empty?
1598
+ # new_node = Erlang::List.allocate
1599
+ # new_node.instance_variable_set(:@head, list.head)
1600
+ # new_node.instance_variable_set(:@improper, improper)
1601
+ # tail.instance_variable_set(:@tail, new_node)
1602
+ # tail.instance_variable_set(:@improper, improper)
1603
+ # tail.immutable!
1604
+ # tail = new_node
1605
+ # list = list.tail
1606
+ # end
1607
+ # tail.instance_variable_set(:@tail, value)
1608
+ # tail.immutable!
1609
+ # return out.tail
1610
+ # end
1611
+
1612
+ # def improper_last(value = nil)
1613
+ # if value.nil?
1614
+ # raise Erlang::ProperListError if not improper?
1615
+ # list = self
1616
+ # list = list.tail while list.tail.is_a?(Erlang::List)
1617
+ # return list.tail
1618
+ # else
1619
+ # self | value
1620
+ # end
1621
+ # end
1622
+
1623
+ # Allows this `Map` to be printed using `Erlang.inspect()`.
1624
+ #
1625
+ # @return [String]
1626
+ def erlang_inspect(raw = false)
1627
+ if improper?
1628
+ result = '['
1629
+ to_proper_list.each_with_index { |obj, i| result << ',' if i > 0; result << Erlang.inspect(obj, raw: raw) }
1630
+ result << '|'
1631
+ result << Erlang.inspect(last(true), raw: raw)
1632
+ result << ']'
1633
+ return result
1634
+ else
1635
+ result = '['
1636
+ each_with_index { |obj, i| result << ',' if i > 0; result << Erlang.inspect(obj, raw: raw) }
1637
+ result << ']'
1638
+ return result
1639
+ end
1640
+ end
1641
+
1642
+ # Return the contents of this `List` as a programmer-readable `String`. If all the
1643
+ # items in the list are serializable as Ruby literal strings, the returned string can
1644
+ # be passed to `eval` to reconstitute an equivalent `List`.
1645
+ #
1646
+ # @return [::String]
18
1647
  def inspect
19
- "#<#{self.class.name} #{super[0..-2]} | #{tail.inspect}]>"
1648
+ if improper?
1649
+ result = 'Erlang::List['
1650
+ list = to_proper_list
1651
+ list.each_with_index { |obj, i| result << ', ' if i > 0; result << obj.inspect }
1652
+ result << ']'
1653
+ result << " + #{last(true).inspect}"
1654
+ return result
1655
+ else
1656
+ result = '['
1657
+ list = self
1658
+ list.each_with_index { |obj, i| result << ', ' if i > 0; result << obj.inspect }
1659
+ result << ']'
1660
+ return result
1661
+ end
20
1662
  end
21
1663
 
22
- def pretty_inspect
23
- "#<#{self.class.name} #{super[0..-2]}>\n"
1664
+ # Allows this `List` to be printed at the `pry` console, or using `pp` (from the
1665
+ # Ruby standard library), in a way which takes the amount of horizontal space on
1666
+ # the screen into account, and which indents nested structures to make them easier
1667
+ # to read.
1668
+ #
1669
+ # @private
1670
+ def pretty_print(pp)
1671
+ if improper?
1672
+ pp.group(1, 'Erlang::List[', '] + ') do
1673
+ pp.breakable ''
1674
+ pp.seplist(to_proper_list) { |obj| obj.pretty_print(pp) }
1675
+ end
1676
+ last(true).pretty_print(pp)
1677
+ else
1678
+ pp.group(1, '[', ']') do
1679
+ pp.breakable ''
1680
+ pp.seplist(self) { |obj| obj.pretty_print(pp) }
1681
+ end
1682
+ end
24
1683
  end
25
1684
 
26
- def pretty_print(q)
27
- q.group(1, '[', " | #{tail.inspect}]") {
28
- q.seplist(self) { |v|
29
- q.pp v
30
- }
31
- }
1685
+ # @private
1686
+ def respond_to?(name, include_private = false)
1687
+ return super || !!name.to_s.match(CADR)
32
1688
  end
33
1689
 
34
- def ==(other)
35
- if improper? and not other.respond_to?(:tail)
36
- false
37
- elsif other.respond_to?(:tail)
38
- super && tail == other.tail
1690
+ private
1691
+ def method_missing(name, *args, &block)
1692
+ if name.to_s.match(CADR)
1693
+ code = "def #{name}; self."
1694
+ code << Regexp.last_match[1].reverse.chars.map do |char|
1695
+ {'a' => 'head', 'd' => 'tail'}[char]
1696
+ end.join('.')
1697
+ code << '; end'
1698
+ List.class_eval(code)
1699
+ send(name, *args, &block)
39
1700
  else
40
1701
  super
41
1702
  end
42
1703
  end
43
1704
  end
44
- end
1705
+ end