Cartesian 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cartesian.rb +40 -4
- data/lib/cartesian_iterator.rb +70 -0
- data/tests/benchmark.rb +8 -5
- data/tests/tc_cartesian.rb +26 -19
- metadata +3 -2
data/lib/cartesian.rb
CHANGED
@@ -30,16 +30,18 @@
|
|
30
30
|
# bar = ["a", "b"]
|
31
31
|
# foo.cartesian(bar) #=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
|
32
32
|
|
33
|
+
require 'cartesian_iterator'
|
34
|
+
|
33
35
|
module Cartesian
|
34
36
|
|
35
37
|
# Produces the cartesian product of self and other.
|
36
38
|
# The result is an array of pairs (i.e. two-element arrays).
|
37
39
|
#
|
38
|
-
#
|
40
|
+
# Cartesian::product( [1,2], %w(A B) ) #=> [[1, "A"], [1, "B"], [2, "A"], [2, "B"]]
|
39
41
|
#
|
40
42
|
# or, if mixed in into Array,
|
41
43
|
#
|
42
|
-
#
|
44
|
+
# [1,2].cartesian %w(A B) #=> [[1, "A"], [1, "B"], [2, "A"], [2, "B"]]
|
43
45
|
#
|
44
46
|
def Cartesian.product(first, second)
|
45
47
|
result = []
|
@@ -59,11 +61,11 @@ module Cartesian
|
|
59
61
|
|
60
62
|
# Behaves as product, except for the elements are joined.
|
61
63
|
#
|
62
|
-
#
|
64
|
+
# Cartesian::joined_cartesian( [1,2], %w(A B) ) #=> ["1A", "1B", "2A", "2B"]
|
63
65
|
#
|
64
66
|
# or, if mixed in into Array,
|
65
67
|
#
|
66
|
-
#
|
68
|
+
# [1,2].joined_cartesian %w(A B) #=> ["1A", "1B", "2A", "2B"]
|
67
69
|
#
|
68
70
|
def Cartesian.joined_product(first, second)
|
69
71
|
product(first, second).map {|pair| pair.join }
|
@@ -74,8 +76,42 @@ module Cartesian
|
|
74
76
|
def joined_cartesian(other)
|
75
77
|
Cartesian.joined_product(self, other)
|
76
78
|
end
|
79
|
+
|
80
|
+
# Convenient way of iterating over the elements.
|
81
|
+
# Preferable when the cartesian product array
|
82
|
+
# is not needed, for the consumption of memory
|
83
|
+
# is fixed and very small, in contrast with the
|
84
|
+
# exponential memory requirements of the
|
85
|
+
# conventional approach.
|
86
|
+
#
|
87
|
+
# for row, col in (1..10).x(1..30)
|
88
|
+
# Matrix[row, col] = row**2 + col**3
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# Of course, calls can be chained as in
|
92
|
+
#
|
93
|
+
# for x, y, z in (1..10).x(1..10).x(1..10)
|
94
|
+
# # ... do something ...
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
#--
|
98
|
+
# for letter, number in %w{a b c}.x(1..3)
|
99
|
+
# ... do something ...
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
#++
|
103
|
+
# Beware that both +self+ and +other+ must implement
|
104
|
+
# +to_a+, i.e., be convertible to array.
|
105
|
+
#
|
106
|
+
def x(other)
|
107
|
+
CartesianIterator.new(self, other)
|
108
|
+
end
|
77
109
|
end
|
78
110
|
|
79
111
|
class Array
|
80
112
|
include Cartesian
|
81
113
|
end
|
114
|
+
|
115
|
+
class Range
|
116
|
+
include Cartesian
|
117
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class CartesianIterator
|
2
|
+
def initialize(foo, bar)
|
3
|
+
@lists = []
|
4
|
+
@tot_iter = 1
|
5
|
+
x(foo)
|
6
|
+
x(bar)
|
7
|
+
end
|
8
|
+
|
9
|
+
def x(other)
|
10
|
+
@lists << other.to_a
|
11
|
+
@tot_iter *= @lists[-1].size
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def each
|
16
|
+
return false if @tot_iter < 1
|
17
|
+
|
18
|
+
elems = []
|
19
|
+
for list in @lists
|
20
|
+
elems << list.restart_and_raw_next
|
21
|
+
end
|
22
|
+
yield *elems
|
23
|
+
|
24
|
+
last_list_index = @lists.size-1
|
25
|
+
n = last_list_index
|
26
|
+
loop do
|
27
|
+
if elems[n] = @lists[n].raw_next
|
28
|
+
yield *elems
|
29
|
+
n = last_list_index
|
30
|
+
next
|
31
|
+
elsif n > 0
|
32
|
+
elems[n] = @lists[n].restart_and_raw_next
|
33
|
+
n -= 1
|
34
|
+
else
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_a
|
41
|
+
array = []
|
42
|
+
self.each {|*element| array << element }
|
43
|
+
array
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module Iterable
|
48
|
+
def start
|
49
|
+
@next_index = -1
|
50
|
+
true
|
51
|
+
end
|
52
|
+
alias :restart :start
|
53
|
+
|
54
|
+
def next
|
55
|
+
@next_index or restart
|
56
|
+
raw_next
|
57
|
+
end
|
58
|
+
|
59
|
+
def raw_next
|
60
|
+
self[@next_index += 1]
|
61
|
+
end
|
62
|
+
|
63
|
+
def restart_and_raw_next
|
64
|
+
self[@next_index = 0]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Array
|
69
|
+
include Iterable
|
70
|
+
end
|
data/tests/benchmark.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'cartesian'
|
2
2
|
require 'benchmark'
|
3
3
|
|
4
4
|
MULTIPLIER = 3
|
@@ -6,11 +6,15 @@ letras = ("a"*MULTIPLIER.."z"*MULTIPLIER).to_a
|
|
6
6
|
numeros = (0..10**1).to_a
|
7
7
|
|
8
8
|
Benchmark.bmbm do |x|
|
9
|
-
|
10
|
-
x.report("
|
11
|
-
end
|
9
|
+
letras_numeros = nil
|
10
|
+
x.report("product") { letras_numeros = Cartesian.product(letras, numeros) }
|
11
|
+
x.report("product 2") { for x,y in letras_numeros; end }
|
12
12
|
|
13
|
+
x.report(".x") { letras_numeros = letras.x(numeros) }
|
14
|
+
x.report(".x 2") { for x,y in letras_numeros; end }
|
15
|
+
end
|
13
16
|
|
17
|
+
#~ x.report("productZip") { Cartesian.productZip(letras, numeros) }
|
14
18
|
|
15
19
|
#~ def Cartesian.productZip(first, second)
|
16
20
|
#~ result = []
|
@@ -20,4 +24,3 @@ end
|
|
20
24
|
#~ end
|
21
25
|
#~ result
|
22
26
|
#~ end
|
23
|
-
|
data/tests/tc_cartesian.rb
CHANGED
@@ -1,23 +1,30 @@
|
|
1
|
-
require '
|
2
|
-
require 'benchmark'
|
1
|
+
require 'test/unit'
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
numeros = (0..10**1).to_a
|
7
|
-
|
8
|
-
Benchmark.bmbm do |x|
|
9
|
-
x.report("product") { Cartesian.product(letras, numeros) }
|
10
|
-
x.report("productZip") { Cartesian.productZip(letras, numeros) }
|
11
|
-
end
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
4
|
+
require 'cartesian'
|
12
5
|
|
6
|
+
class TestCartesian < Test::Unit::TestCase
|
7
|
+
def test_arrays
|
8
|
+
foo = [1,2,3]
|
9
|
+
bar = %w{a b c}
|
10
|
+
expected = [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"],
|
11
|
+
[2, "c"], [3, "a"], [3, "b"], [3, "c"]]
|
12
|
+
assert(foo.x(bar).to_a == expected)
|
13
|
+
end
|
13
14
|
|
15
|
+
def test_ranges
|
16
|
+
foo = 1..3
|
17
|
+
bar = 4..6
|
18
|
+
expected = [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5],
|
19
|
+
[2, 6], [3, 4], [3, 5], [3, 6]]
|
20
|
+
assert(foo.x(bar).to_a == expected)
|
21
|
+
end
|
14
22
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
def test_mixed
|
24
|
+
foo = 1..3
|
25
|
+
bar = %w{a b c}
|
26
|
+
expected = [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"],
|
27
|
+
[2, "c"], [3, "a"], [3, "b"], [3, "c"]]
|
28
|
+
assert(foo.x(bar).to_a == expected)
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: Cartesian
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1
|
7
|
-
date:
|
6
|
+
version: 0.2.1
|
7
|
+
date: 2007-01-07 00:00:00 -03:00
|
8
8
|
summary: The Cartesian module provide methods for the calculation of the cartesian producted between two enumberable objects. It can also be easily mixed in into any enumberable class, i.e. any class with Enumerable module mixed in.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -32,6 +32,7 @@ files:
|
|
32
32
|
- tests/benchmark.rb
|
33
33
|
- tests/tc_cartesian.rb
|
34
34
|
- lib/cartesian.rb
|
35
|
+
- lib/cartesian_iterator.rb
|
35
36
|
test_files:
|
36
37
|
- tests/tc_cartesian.rb
|
37
38
|
rdoc_options: []
|