erlang-terms 1.1.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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