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.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.editorconfig +20 -0
- data/.gitignore +10 -18
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +15 -3
- data/.yardopts +6 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +21 -1
- data/LICENSE.txt +1 -1
- data/README.md +95 -17
- data/Rakefile +8 -3
- data/erlang-terms.gemspec +14 -11
- data/lib/erlang-terms.rb +1 -0
- data/lib/erlang/associable.rb +98 -0
- data/lib/erlang/atom.rb +257 -0
- data/lib/erlang/binary.rb +425 -0
- data/lib/erlang/bitstring.rb +464 -0
- data/lib/erlang/cons.rb +122 -0
- data/lib/erlang/enumerable.rb +160 -0
- data/lib/erlang/error.rb +4 -0
- data/lib/erlang/export.rb +110 -12
- data/lib/erlang/float.rb +201 -0
- data/lib/erlang/function.rb +259 -0
- data/lib/erlang/immutable.rb +101 -0
- data/lib/erlang/list.rb +1685 -24
- data/lib/erlang/map.rb +935 -21
- data/lib/erlang/nil.rb +73 -10
- data/lib/erlang/pid.rb +120 -18
- data/lib/erlang/port.rb +123 -0
- data/lib/erlang/reference.rb +161 -0
- data/lib/erlang/string.rb +175 -3
- data/lib/erlang/term.rb +24 -0
- data/lib/erlang/terms.rb +324 -8
- data/lib/erlang/terms/version.rb +1 -1
- data/lib/erlang/trie.rb +364 -0
- data/lib/erlang/tuple.rb +1582 -14
- data/lib/erlang/undefined.rb +32 -0
- metadata +49 -71
- data/spec/erlang/export_spec.rb +0 -17
- data/spec/erlang/list_spec.rb +0 -39
- data/spec/erlang/map_spec.rb +0 -24
- data/spec/erlang/nil_spec.rb +0 -18
- data/spec/erlang/pid_spec.rb +0 -21
- data/spec/erlang/string_spec.rb +0 -11
- data/spec/erlang/terms_spec.rb +0 -7
- data/spec/erlang/tuple_spec.rb +0 -20
- 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
|
data/lib/erlang/list.rb
CHANGED
@@ -1,44 +1,1705 @@
|
|
1
1
|
module Erlang
|
2
|
-
class
|
3
|
-
|
2
|
+
class ImproperListError < Erlang::Error; end
|
3
|
+
class ProperListError < Erlang::Error; end
|
4
4
|
|
5
|
-
|
6
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
23
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|