immutable 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -3,37 +3,47 @@ immutable - immutable data structures for Ruby
3
3
 
4
4
  This project aims to provide immutable data structures for Ruby.
5
5
 
6
- Currently, immutable provides the following classes:
6
+ Currently, immutable provides the following classes and modules:
7
7
 
8
+ * Immutable::Foldable
8
9
  * Immutable::List
9
10
  * Immutable::Map
11
+ * Immutable::Promise
12
+ * Immutable::Stream
13
+ * Immutable::Queue
10
14
 
11
15
  Install
12
16
  ========================
13
17
 
14
18
  $ gem install immutable
15
19
 
20
+ Documentation
21
+ ========================
22
+
23
+ * [API Reference](http://rubydoc.info/github/shugo/immutable/frames)
24
+
16
25
  License
17
26
  ========================
18
- (The MIT License)
19
-
20
- Copyright (c) 2012 Shugo Maeda
21
-
22
- Permission is hereby granted, free of charge, to any person obtaining
23
- a copy of this software and associated documentation files (the
24
- 'Software'), to deal in the Software without restriction, including
25
- without limitation the rights to use, copy, modify, merge, publish,
26
- distribute, sublicense, and/or sell copies of the Software, and to
27
- permit persons to whom the Software is furnished to do so, subject to
28
- the following conditions:
29
-
30
- The above copyright notice and this permission notice shall be
31
- included in all copies or substantial portions of the Software.
32
-
33
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
34
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
36
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
37
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
38
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+
28
+ (The MIT License)
29
+
30
+ Copyright (c) 2012 Shugo Maeda
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining
33
+ a copy of this software and associated documentation files (the
34
+ 'Software'), to deal in the Software without restriction, including
35
+ without limitation the rights to use, copy, modify, merge, publish,
36
+ distribute, sublicense, and/or sell copies of the Software, and to
37
+ permit persons to whom the Software is furnished to do so, subject to
38
+ the following conditions:
39
+
40
+ The above copyright notice and this permission notice shall be
41
+ included in all copies or substantial portions of the Software.
42
+
43
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
44
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
45
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
46
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
47
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
48
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
49
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ require 'benchmark'
2
+ require 'immutable/queue'
3
+ require 'immutable/map'
4
+
5
+ TIMES = 100000
6
+ key_size = 10
7
+
8
+ def snoc(queue)
9
+ TIMES.times do
10
+ queue.snoc(0)
11
+ end
12
+ end
13
+
14
+ def head(queue)
15
+ TIMES.times do
16
+ queue.head
17
+ end
18
+ end
19
+
20
+ def tail(queue)
21
+ TIMES.times do
22
+ queue.tail
23
+ end
24
+ end
25
+
26
+ def run(bm, queue, num, method)
27
+ bm.report("#{method} for #{num}-elements queue") do
28
+ send(method, queue)
29
+ end
30
+ end
31
+
32
+ Benchmark.bmbm do |bm|
33
+ queues = [10, 1000, 100000].inject(Immutable::Map.empty) { |m, n|
34
+ m.insert(n, (1..n).inject(Immutable::Queue.empty, &:snoc))
35
+ }
36
+ for method in [:snoc, :head, :tail]
37
+ for num, queue in queues
38
+ run(bm, queue, num, method)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ # +Immutable+ is a namespace for immutable data structures.
2
+ module Immutable
3
+ end
4
+
5
+ require "immutable/list"
6
+ require "immutable/map"
7
+ require "immutable/promise"
8
+ require "immutable/stream"
9
+ require "immutable/queue"
@@ -0,0 +1,29 @@
1
+ module Immutable
2
+ module Foldable
3
+ # Returns the number of elements in +self+. May be zero.
4
+ #
5
+ # @return [Integer] the number of elements in +self+.
6
+ def length
7
+ foldl(0) { |x, y| x + 1 }
8
+ end
9
+
10
+ # Returns the number of elements in +self+. May be zero.
11
+ #
12
+ # @return [Integer] the number of elements in +self+.
13
+ alias size length
14
+
15
+ # Computes the sum of the numbers in +self+.
16
+ #
17
+ # @return [#+] the sum of the numbers.
18
+ def sum
19
+ foldl(0, &:+)
20
+ end
21
+
22
+ # Computes the product of the numbers in +self+.
23
+ #
24
+ # @return [#*] the product of the numbers.
25
+ def product
26
+ foldl(1, &:*)
27
+ end
28
+ end
29
+ end
@@ -1,121 +1,167 @@
1
- # -*- tailcall-optimization: true; trace-instruction: false -*-
1
+ require "immutable/foldable"
2
2
 
3
3
  module Immutable
4
- # <code>Immutable::List</code> represents an immutable list.
4
+ # +Immutable::List+ represents an immutable list.
5
5
  #
6
- # <code>Immutable::List</code> is an abstract class and
7
- # <code>Immutable::List.[]</code> should be used instead of
8
- # <code>Immutable::List.new</code>. For example:
6
+ # +Immutable::List+ is an abstract class and
7
+ # {Immutable::List.[]} should be used instead of
8
+ # {Immutable::List.new}. For example:
9
9
  #
10
10
  # include Immutable
11
11
  # p List[] #=> List[]
12
12
  # p List[1, 2] #=> List[1, 2]
13
13
  #
14
- # <code>Immutable::Nil</code> represents an empty list, and
15
- # <code>Immutable::Cons</code> represents a cons cell, which is a node in
14
+ # +Immutable::Nil+ represents an empty list, and
15
+ # +Immutable::Cons+ represents a cons cell, which is a node in
16
16
  # a list. For example:
17
17
  #
18
18
  # p Nil #=> List[]
19
19
  # p Cons[1, Cons[2, Nil]] #=> List[1, 2]
20
20
  class List
21
+ include Enumerable
22
+ include Foldable
23
+
21
24
  class EmptyError < StandardError
22
25
  def initialize(msg = "list is empty")
23
26
  super(msg)
24
27
  end
25
28
  end
26
29
 
27
- # Returns a new list populated with the given objects.
28
- def self.[](*args)
29
- from_array(args)
30
+ # Creates a new list populated with the given objects.
31
+ #
32
+ # @param [Array<Object>] elements the elements of the list.
33
+ # @return [List] the new list.
34
+ def self.[](*elements)
35
+ from_array(elements)
30
36
  end
31
37
 
32
- # Converts the given array +ary+ to a list.
38
+ # Converts the given array to a list.
39
+ #
40
+ # @param [Array, #reverse_each] ary the array to convert.
41
+ # @return [List] the list converted from +ary+.
33
42
  def self.from_array(ary)
34
43
  ary.reverse_each.inject(Nil) { |x, y|
35
44
  Cons.new(y, x)
36
45
  }
37
46
  end
38
47
 
39
- # Converts +enum+ to a list. +enum+ should respond to <code>each</code>.
48
+ # Converts the given Enumerable object to a list.
49
+ #
50
+ # @param [#inject] enum the Enumerable object to convert.
51
+ # @return [List] the list converted from +enum+.
40
52
  def self.from_enum(enum)
41
53
  enum.inject(Nil) { |x, y|
42
54
  Cons.new(y, x)
43
55
  }.reverse
44
56
  end
45
57
 
58
+ # Calls +block+ once for each element in +self+.
59
+ def each(&block)
60
+ end
61
+
46
62
  # Appends two lists +self+ and +xs+.
63
+ #
64
+ # @param [List] xs the list to append.
65
+ # @return [List] the new list.
47
66
  def +(xs)
48
67
  foldr(xs) { |y, ys| Cons[y, ys] }
49
68
  end
50
69
 
70
+ # Returns whether +self+ equals to +xs+.
71
+ #
72
+ # @param [List] xs the list to compare.
73
+ # @return [true, false] +true+ if +self+ equals to +xs+; otherwise,
74
+ # +false+.
75
+ def ==(xs)
76
+ # this method should be overriden
77
+ end
78
+
51
79
  # Returns the first element of +self+. If +self+ is empty,
52
- # <code>Immutable::List::EmptyError</code> is raised.
80
+ # +Immutable::List::EmptyError+ is raised.
81
+ #
82
+ # @return [Object] the first element of +self+.
53
83
  def head
54
- raise ScriptError, "this method should be overriden"
84
+ # this method should be overriden
55
85
  end
56
86
 
57
87
  # Returns the first element of +self+. If +self+ is empty,
58
- # <code>Immutable::List::EmptyError</code> is raised.
88
+ # +Immutable::List::EmptyError+ is raised.
89
+ #
90
+ # @return [Object] the first element of +self+.
59
91
  def first
60
92
  head
61
93
  end
62
94
 
63
95
  # Returns the last element of +self+. If +self+ is empty,
64
- # <code>Immutable::List::EmptyError</code> is raised.
96
+ # +Immutable::List::EmptyError+ is raised.
97
+ #
98
+ # @return [Object] the last element of +self+.
65
99
  def last
66
- raise ScriptError, "this method should be overriden"
100
+ # this method should be overriden
67
101
  end
68
102
 
69
- # Returns the element after the head of +self+. If +self+ is empty,
70
- # <code>Immutable::List::EmptyError</code> is raised.
103
+ # Returns the elements after the head of +self+. If +self+ is empty,
104
+ # +Immutable::List::EmptyError+ is raised.
105
+ #
106
+ # @return [List] the elements after the head of +self+.
71
107
  def tail
72
- raise ScriptError, "this method should be overriden"
108
+ # this method should be overriden
73
109
  end
74
110
 
75
- # Returns all the element of +self+ except the last one.
76
- # If +self+ is empty, <code>Immutable::List::EmptyError</code> is
111
+ # Returns all the elements of +self+ except the last one.
112
+ # If +self+ is empty, +Immutable::List::EmptyError+ is
77
113
  # raised.
114
+ #
115
+ # @return [List] the elements of +self+ except the last one.
78
116
  def init
79
- raise ScriptError, "this method should be overriden"
117
+ # this method should be overriden
80
118
  end
81
119
 
82
- # Returns <code>true</code> if +self+ is empty.
120
+ # Returns whether +self+ is empty.
121
+ #
122
+ # @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
83
123
  def empty?
84
- raise ScriptError, "this method should be overriden"
124
+ # this method should be overriden
85
125
  end
86
126
 
87
- # Returns <code>true</code> if +self+ is empty.
127
+ # Returns whether +self+ is empty.
128
+ #
129
+ # @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
88
130
  def null?
89
131
  empty?
90
132
  end
91
133
 
92
- # Returns the number of elements in +self+. May be zero.
93
- def length
94
- foldl(0) { |x, y| x + 1 }
95
- end
96
-
97
- alias size length
98
-
99
134
  # Returns the list obtained by applying the given block to each element
100
135
  # in +self+.
136
+ #
137
+ # @return [List] the obtained list.
101
138
  def map
102
139
  foldr(Nil) { |x, xs| Cons[yield(x), xs] }
103
140
  end
104
141
 
105
142
  # Returns the elements of +self+ in reverse order.
143
+ #
144
+ # @return [List] the reversed list.
106
145
  def reverse
107
146
  foldl(Nil) { |x, y| Cons[y, x] }
108
147
  end
109
148
 
110
- # Inserts +xs+ in between the lists in +self+.
111
- def intersperse(xs)
112
- raise ScriptError, "this method should be overriden"
149
+ # Returns a new list obtained by inserting +sep+ in between the elements
150
+ # of +self+.
151
+ #
152
+ # @param [Object] sep the object to insert between elements.
153
+ # @return [List] the new list.
154
+ def intersperse(sep)
155
+ # this method should be overriden
113
156
  end
114
157
 
115
- # Inserts +xs+ in between the lists in +self+ and concatenates the
116
- # result.
117
- # <code>xss.intercalate(xs)</code> is equivalent to
118
- # <code>xss.intersperse(xs).flatten</code>.
158
+ # Returns a new list obtained by inserting +xs+ in between the lists in
159
+ # +self+ and concatenates the result.
160
+ # +xss.intercalate(xs)+ is equivalent to
161
+ # +xss.intersperse(xs).flatten+.
162
+ #
163
+ # @param [List] xs the list to insert between lists.
164
+ # @return [List] the new list.
119
165
  def intercalate(xs)
120
166
  intersperse(xs).flatten
121
167
  end
@@ -124,78 +170,91 @@ module Immutable
124
170
  #
125
171
  # p List[List[1, 2, 3], List[4, 5, 6]].transpose
126
172
  # #=> List[List[1, 4], List[2, 5], List[3, 6]]
173
+ #
174
+ # @return [List] the transposed list.
127
175
  def transpose
128
- raise ScriptError, "this method should be overriden"
176
+ # this method should be overriden
177
+ end
178
+
179
+ # Returns the list of all subsequences of +self+.
180
+ #
181
+ # @return [List<List>] the list of subsequences.
182
+ def subsequences
183
+ Cons[List[], nonempty_subsequences]
129
184
  end
130
185
 
131
186
  # Reduces +self+ using +block+ from right to left. +e+ is used as the
132
187
  # starting value. For example:
133
188
  #
134
189
  # List[1, 2, 3].foldr(9) { |x, y| x + y } #=> 1 - (2 - (3 - 9)) = -7
190
+ #
191
+ # @param [Object] e the start value.
192
+ # @return [Object] the reduced value.
135
193
  def foldr(e, &block)
136
- raise ScriptError, "this method should be overriden"
194
+ # this method should be overriden
137
195
  end
138
196
 
139
197
  # Reduces +self+ using +block+ from right to left. If +self+ is empty,
140
- # <code>Immutable::List::EmptyError</code> is raised.
198
+ # +Immutable::List::EmptyError+ is raised.
199
+ #
200
+ # @return [Object] the reduced value.
141
201
  def foldr1(&block)
142
- raise ScriptError, "this method should be overriden"
202
+ # this method should be overriden
143
203
  end
144
204
 
145
205
  # Reduces +self+ using +block+ from left to right. +e+ is used as the
146
206
  # starting value. For example:
147
207
  #
148
208
  # List[1, 2, 3].foldl(9) { |x, y| x + y } #=> ((9 - 1) - 2) - 3 = 3
209
+ #
210
+ # @param [Object] e the start value.
211
+ # @return [Object] the reduced value.
149
212
  def foldl(e, &block)
150
- raise ScriptError, "this method should be overriden"
213
+ # this method should be overriden
151
214
  end
152
215
 
153
216
  # Reduces +self+ using +block+ from left to right. If +self+ is empty,
154
- # <code>Immutable::List::EmptyError</code> is raised.
217
+ # +Immutable::List::EmptyError+ is raised.
218
+ #
219
+ # @return [Object] the reduced value.
155
220
  def foldl1(&block)
156
- raise ScriptError, "this method should be overriden"
221
+ # this method should be overriden
157
222
  end
158
223
 
159
224
  # Concatenates a list of lists.
225
+ #
226
+ # @return [List] the concatenated list.
160
227
  def flatten
161
228
  foldr(Nil) { |x, xs| x + xs }
162
229
  end
163
230
 
164
- # An alias of <code>flatten</code>.
165
231
  alias concat flatten
166
232
 
167
233
  # Returns the list obtained by concatenating the results of the given
168
234
  # block for each element in +self+.
235
+ #
236
+ # @return [List] the obtained list.
169
237
  def flat_map
170
238
  foldr(Nil) { |x, xs| yield(x) + xs }
171
239
  end
172
240
 
173
- # An alias of <code>flat_map</code>.
174
241
  alias concat_map flat_map
175
242
 
176
- # An alias of <code>flat_map</code>.
177
243
  alias bind flat_map
178
244
 
179
- # Computes the sum of the numbers in +self+.
180
- def sum
181
- foldl(0, &:+)
182
- end
183
-
184
- # Computes the product of the numbers in +self+.
185
- def product
186
- foldl(1, &:*)
187
- end
188
-
189
245
  # Builds a list from the seed value +e+ and the given block. The block
190
- # takes a seed value and returns <code>nil</code> if the seed should
191
- # unfold to the empty list, or returns <code>[a, b]</code>, where
192
- # <code>a</code> is the head of the list and <code>b</code> is the next
246
+ # takes a seed value and returns +nil+ if the seed should
247
+ # unfold to the empty list, or returns +[a, b]+, where
248
+ # +a+ is the head of the list and +b+ is the next
193
249
  # seed from which to unfold the tail. For example:
194
250
  #
195
251
  # xs = List.unfoldr(3) { |x| x == 0 ? nil : [x, x - 1] }
196
252
  # p xs #=> List[3, 2, 1]
197
253
  #
198
- # <code>unfoldr</code> is the dual of <code>foldr</code>.
254
+ # +unfoldr+ is the dual of +foldr+.
255
+ #
256
+ # @param [Object] e the seed value.
257
+ # @return [List] the list built from the seed value and the block.
199
258
  def self.unfoldr(e, &block)
200
259
  x = yield(e)
201
260
  if x.nil?
@@ -206,40 +265,68 @@ module Immutable
206
265
  end
207
266
  end
208
267
 
209
- # Returns the first +n+ elements of +self+, or +self+ itself if
210
- # <code>n > self.length</code>.
211
- def take(n)
212
- raise ScriptError, "this method should be overriden"
268
+ # Returns the +n+th element of +self+. If +n+ is out of range, +nil+ is
269
+ # returned.
270
+ #
271
+ # @return [Object] the +n+th element.
272
+ def [](n)
273
+ # this method should be overriden
213
274
  end
214
275
 
276
+ # Returns the first +n+ elements of +self+, or all the elements of
277
+ # +self+ if +n > self.length+.
278
+ #
279
+ # @param [Integer] n the number of elements to take.
280
+ # @return [List] the first +n+ elements of +self+.
281
+ def take(n)
282
+ # this method should be overriden
283
+ end
215
284
 
216
285
  # Returns the suffix of +self+ after the first +n+ elements, or
217
- # <code>List[]</code> if <code>n > self.length</code>.
286
+ # +List[]+ if +n > self.length+.
287
+ #
288
+ # @param [Integer] n the number of elements to drop.
289
+ # @return [List] the suffix of +self+ after the first +n+ elements.
218
290
  def drop(n)
219
- raise ScriptError, "this method should be overriden"
291
+ # this method should be overriden
220
292
  end
221
293
 
222
294
  # Returns the longest prefix of the elements of +self+ for which +block+
223
295
  # evaluates to true.
296
+ #
297
+ # @return [List] the prefix of the elements of +self+.
224
298
  def take_while(&block)
225
- raise ScriptError, "this method should be overriden"
299
+ # this method should be overriden
226
300
  end
227
301
 
228
302
  # Returns the suffix remaining after
229
- # <code>self.take_while(&block)</code>.
303
+ # +self.take_while(&block)+.
304
+ #
305
+ # @return [List] the suffix of the elements of +self+.
230
306
  def drop_while(&block)
231
- raise ScriptError, "this method should be overriden"
307
+ # this method should be overriden
308
+ end
309
+
310
+ # Returns +self+.
311
+ #
312
+ # @return [List] +self+.
313
+ def to_list
314
+ self
232
315
  end
233
316
 
234
317
  # Returns the first element in +self+ for which the given block
235
318
  # evaluates to true. If such an element is not found, it
236
- # returns <code>nil</code>.
319
+ # returns +nil+.
320
+ #
321
+ # @return [Object] the found element.
237
322
  def find(&block)
238
- raise ScriptError, "this method should be overriden"
323
+ # this method should be overriden
239
324
  end
240
325
 
241
326
  # Returns the elements in +self+ for which the given block evaluates to
242
327
  # true.
328
+ #
329
+ # @return [List] the elements that satisfies the condition.
243
330
  def filter
244
331
  foldr(Nil) { |x, xs|
245
332
  if yield(x)
@@ -249,12 +336,38 @@ module Immutable
249
336
  end
250
337
  }
251
338
  end
339
+
340
+ # Takes zero or more lists and returns a new list in which each element
341
+ # is an array of the corresponding elements of +self+ and the input
342
+ # lists.
343
+ #
344
+ # @param [Array<List>] xss the input lists.
345
+ # @return [List] the new list.
346
+ def zip(*xss)
347
+ # this method should be overriden
348
+ end
349
+
350
+ # Takes zero or more lists and returns the list obtained by applying the
351
+ # given block to an array of the corresponding elements of +self+ and
352
+ # the input lists.
353
+ # +xs.zip_with(*yss, &block)+ is equivalent to
354
+ # +xs.zip(*yss).map(&block)+.
355
+ #
356
+ # @param [Array<List>] xss the input lists.
357
+ # @return [List] the new list.
358
+ def zip_with(*xss, &block)
359
+ # this method should be overriden
360
+ end
252
361
  end
253
362
 
363
+ # +Immutable::Nil+ represents an empty list.
254
364
  Nil = List.new
255
365
 
366
+ # +Immutable::Cons+ represents a cons cell.
256
367
  class Cons < List
257
- # Creates a list obtained by prepending +head+ to the list +tail+.
368
+ # Creates a new list obtained by prepending +head+ to the list +tail+.
369
+ #
370
+ # @return [Cons] the new list.
258
371
  def self.[](head, tail = Nil)
259
372
  self.new(head, tail)
260
373
  end
@@ -329,6 +442,14 @@ module Immutable
329
442
  end
330
443
  end
331
444
 
445
+ def Nil.each
446
+ end
447
+
448
+ def each(&block)
449
+ yield(@head)
450
+ @tail.each(&block)
451
+ end
452
+
332
453
  def Nil.foldl(e)
333
454
  e
334
455
  end
@@ -345,8 +466,12 @@ module Immutable
345
466
  @tail.foldl(@head, &block)
346
467
  end
347
468
 
469
+ def Nil.==(xs)
470
+ equal?(xs)
471
+ end
472
+
348
473
  def ==(xs)
349
- if xs.empty?
474
+ if !xs.is_a?(List) || xs.empty?
350
475
  false
351
476
  else
352
477
  @head == xs.head && @tail == xs.tail
@@ -392,6 +517,31 @@ module Immutable
392
517
  end
393
518
  end
394
519
 
520
+ def Nil.nonempty_subsequences
521
+ List[]
522
+ end
523
+
524
+ def nonempty_subsequences
525
+ yss = @tail.nonempty_subsequences.foldr(List[]) { |xs, xss|
526
+ Cons[xs, Cons[Cons[@head, xs], xss]]
527
+ }
528
+ Cons[List[@head], yss]
529
+ end
530
+
531
+ def Nil.[](n)
532
+ nil
533
+ end
534
+
535
+ def [](n)
536
+ if n < 0
537
+ nil
538
+ elsif n == 0
539
+ head
540
+ else
541
+ tail[n - 1]
542
+ end
543
+ end
544
+
395
545
  def Nil.take(n)
396
546
  Nil
397
547
  end
@@ -451,5 +601,25 @@ module Immutable
451
601
  @tail.find(&block)
452
602
  end
453
603
  end
604
+
605
+ def Nil.zip(*xss)
606
+ Nil
607
+ end
608
+
609
+ def zip(*xss)
610
+ heads = xss.map { |xs| xs.null? ? nil : xs.head }
611
+ tails = xss.map { |xs| xs.null? ? Nil : xs.tail }
612
+ Cons[[head, *heads], tail.zip(*tails)]
613
+ end
614
+
615
+ def Nil.zip_with(*xss, &block)
616
+ Nil
617
+ end
618
+
619
+ def zip_with(*xss, &block)
620
+ heads = xss.map { |xs| xs.null? ? nil : xs.head }
621
+ tails = xss.map { |xs| xs.null? ? Nil : xs.tail }
622
+ Cons[yield(head, *heads), tail.zip_with(*tails, &block)]
623
+ end
454
624
  end
455
625
  end