hamster 0.1.11 → 0.1.12
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +6 -0
- data/README.rdoc +41 -10
- data/lib/hamster/hash.rb +7 -5
- data/lib/hamster/list.rb +17 -2
- data/lib/hamster/set.rb +7 -5
- data/lib/hamster/stack.rb +11 -6
- data/lib/hamster/version.rb +1 -1
- data/spec/hamster/hash/any_spec.rb +35 -31
- data/spec/hamster/hash/each_spec.rb +3 -3
- data/spec/hamster/hash/filter_spec.rb +3 -3
- data/spec/hamster/hash/map_spec.rb +3 -7
- data/spec/hamster/hash/reject_spec.rb +3 -3
- data/spec/hamster/list/construction_spec.rb +29 -0
- data/spec/hamster/list/inspect_spec.rb +27 -0
- data/spec/hamster/list/map_spec.rb +24 -20
- data/spec/hamster/list/to_a_spec.rb +27 -0
- data/spec/hamster/set/any_spec.rb +33 -29
- data/spec/hamster/set/each_spec.rb +3 -3
- data/spec/hamster/set/filter_spec.rb +3 -3
- data/spec/hamster/set/map_spec.rb +3 -7
- data/spec/hamster/set/reject_spec.rb +3 -3
- data/spec/hamster/stack/copying_spec.rb +5 -9
- metadata +5 -4
- data/spec/hamster/list/lazy_spec.rb +0 -21
data/History.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -41,6 +41,8 @@ The same goes for <tt>#remove</tt>:
|
|
41
41
|
|
42
42
|
As mentioned earlier, persistent data structures perform a copy whenever they are modified meaning there is never any chance that two threads could be modifying the same instance at any one time. And, because they are very efficient copies, you don't need to worry about using up gobs of heap space in the process.
|
43
43
|
|
44
|
+
Moreover, because they're immutable, you can pass them around between objects, methods, and functions and never worry about data corruption; no more defensive calls to <tt>collection.dup</tt>!
|
45
|
+
|
44
46
|
== OK, that sounds mildly interesting. What's the downside--there's always a downside?
|
45
47
|
|
46
48
|
There's a potential performance hit when compared with MRI's built-in, native, hand-crafted C-code implementation of <tt>Hash</tt>. For example:
|
@@ -89,7 +91,7 @@ The <tt>Hamster::Hash</tt> version on the other hand was unchanged from the orig
|
|
89
91
|
|
90
92
|
Well, I could show you one but I'd have to re-write--or at least wrap--most <tt>Hash</tt> methods to make it generic, or at least write some application-specific code that synchronised using a <tt>Mutex</tt> and ... well ... it's hard, I always make mistakes, I always end up with weird edge cases and race conditions so, I'll leave that as an exercise for you :)
|
91
93
|
|
92
|
-
And that
|
94
|
+
And don't forget that even if threading isn't a concern for you, the safety provided by immutability is worth it.
|
93
95
|
|
94
96
|
== So, you mentioned Sets, Lists, and Stacks?
|
95
97
|
|
@@ -97,20 +99,49 @@ Indeed I did.
|
|
97
99
|
|
98
100
|
=== Lists
|
99
101
|
|
100
|
-
list
|
102
|
+
Lists have a head--the value of the item at the head of the list--and a tail--containing the remaining items. For example:
|
103
|
+
|
104
|
+
list = Hamster.list(1, 2, 3)
|
105
|
+
|
106
|
+
list.head # => 1
|
107
|
+
list.tail # => Hamster.list(2, 3)
|
108
|
+
|
109
|
+
To add to a list you use <tt>#cons</tt>:
|
110
|
+
|
111
|
+
original = Hamster.list(1, 2, 3)
|
112
|
+
copy = original.cons(0) # => Hamster.list(0, 1, 2, 3)
|
113
|
+
|
114
|
+
Notice how modifying a list actually returns a new list. That's because Hamster lists are immutable. Thankfully, just like Hamster <tt>Set</tt> and <tt>Hash</tt>, they're also very efficient at making copies!
|
115
|
+
|
116
|
+
Lists are, where possible, lazy. That is, they try to defer processing items until absolutely necessary. For example, given a crude function to detect prime numbers:
|
117
|
+
|
118
|
+
def prime?(n)
|
119
|
+
2.upto(Math.sqrt(n).round) { |i| return false if n % i == 0 }
|
120
|
+
true
|
121
|
+
end
|
101
122
|
|
102
|
-
|
103
|
-
list.tail # => Hamster.list(2, 3)
|
123
|
+
The following code will only call <tt>#prime?</tt> as many times as necessary to generate the first 3 prime numbers between 10000 and 1000000:
|
104
124
|
|
105
|
-
|
106
|
-
2.upto(Math.sqrt(n).round) { |i| return false if n % i == 0 }
|
107
|
-
true
|
108
|
-
end
|
125
|
+
Hamster.interval(10000, 1000000).filter { |i| prime?(i) }.take(3) # => 0.0009s
|
109
126
|
|
110
|
-
|
127
|
+
Compare that to the conventional equivalent which needs to calculate all possible values in the range before taking the first 3:
|
128
|
+
|
129
|
+
(10000..1000000).select { |i| prime?(i) }[1, 3] # => 10s
|
130
|
+
|
131
|
+
Besides <tt>Hamster.list</tt> there is <tt>Hamster.interval(from, to)</tt> and <tt>Hamster.stream { a_block }</tt>.
|
132
|
+
|
133
|
+
<tt>.interval</tt> (aliased as <tt>.range</tt>) creates a lazy list equivalent to a list containing all the values between <tt>from</tt> and <tt>to</tt> without actually creating a list that big.
|
134
|
+
|
135
|
+
<tt>.stream</tt> allows you to creates infinite lists. Each time a new value is required, the supplied block is called.
|
111
136
|
|
112
137
|
=== Stacks
|
113
138
|
|
114
139
|
=== Sets
|
115
140
|
|
116
|
-
|
141
|
+
== Disclaimer
|
142
|
+
|
143
|
+
Hamster started out as a spike to prove a point and has since morphed into something I actually use. My primary concern has been to round out the functionality with good test coverage and clean, readable code.
|
144
|
+
|
145
|
+
Performance is pretty good--especially with lazy lists--but there are some things which need to be converted from recursive to iterative due to a lack of Tail-Call-Optimisation in Ruby. It's on the list ;-)
|
146
|
+
|
147
|
+
Documentation is sparse but I've tried as best I can to write specs that reads as documentation. I've also tried to alias methods as their <tt>Enumerable</tt> equivalents where possible to make it easier for people to migrate code.
|
data/lib/hamster/hash.rb
CHANGED
@@ -3,7 +3,7 @@ require 'hamster/trie'
|
|
3
3
|
module Hamster
|
4
4
|
|
5
5
|
def self.hash(pairs = {})
|
6
|
-
pairs.
|
6
|
+
pairs.inject(Hash.new) { |hash, pair| hash.put(pair.first, pair.last) }
|
7
7
|
end
|
8
8
|
|
9
9
|
class Hash
|
@@ -51,13 +51,13 @@ module Hamster
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def each
|
54
|
-
block_given? or return
|
54
|
+
block_given? or return self
|
55
55
|
@trie.each { |entry| yield(entry.key, entry.value) }
|
56
56
|
self
|
57
57
|
end
|
58
58
|
|
59
59
|
def map
|
60
|
-
block_given? or return
|
60
|
+
block_given? or return self
|
61
61
|
if empty?
|
62
62
|
self
|
63
63
|
else
|
@@ -73,7 +73,7 @@ module Hamster
|
|
73
73
|
alias_method :inject, :reduce
|
74
74
|
|
75
75
|
def filter
|
76
|
-
block_given? or return
|
76
|
+
block_given? or return self
|
77
77
|
trie = @trie.filter { |entry| yield(entry.key, entry.value) }
|
78
78
|
if !trie.equal?(@trie)
|
79
79
|
self.class.new(trie)
|
@@ -84,7 +84,7 @@ module Hamster
|
|
84
84
|
alias_method :select, :filter
|
85
85
|
|
86
86
|
def reject
|
87
|
-
block_given? or return
|
87
|
+
block_given? or return self
|
88
88
|
select { |key, value| !yield(key, value) }
|
89
89
|
end
|
90
90
|
|
@@ -96,6 +96,8 @@ module Hamster
|
|
96
96
|
end
|
97
97
|
false
|
98
98
|
end
|
99
|
+
alias_method :exist?, :any?
|
100
|
+
alias_method :exists?, :any?
|
99
101
|
|
100
102
|
def all?
|
101
103
|
if block_given?
|
data/lib/hamster/list.rb
CHANGED
@@ -3,7 +3,12 @@ module Hamster
|
|
3
3
|
class << self
|
4
4
|
|
5
5
|
def list(*items)
|
6
|
-
items.reverse.
|
6
|
+
items.reverse.inject(EmptyList) { |list, item| list.cons(item) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def stream(&block)
|
10
|
+
block_given? or return EmptyList
|
11
|
+
Stream.new(yield) { stream(&block) }
|
7
12
|
end
|
8
13
|
|
9
14
|
def interval(from, to)
|
@@ -43,6 +48,7 @@ module Hamster
|
|
43
48
|
block_given? or return self
|
44
49
|
Stream.new(yield(head)) { tail.map(&block) }
|
45
50
|
end
|
51
|
+
alias_method :collect, :map
|
46
52
|
|
47
53
|
def reduce(memo, &block)
|
48
54
|
block_given? or return memo
|
@@ -120,6 +126,14 @@ module Hamster
|
|
120
126
|
end
|
121
127
|
alias_method :clone, :dup
|
122
128
|
|
129
|
+
def to_a
|
130
|
+
reduce([]) { |ary, item| ary << item }
|
131
|
+
end
|
132
|
+
|
133
|
+
def inspect
|
134
|
+
to_a.inspect
|
135
|
+
end
|
136
|
+
|
123
137
|
private
|
124
138
|
|
125
139
|
def method_missing(name, *args, &block)
|
@@ -135,7 +149,7 @@ module Hamster
|
|
135
149
|
# identify the series of car and cdr operations that is performed by the function. The order in which the 'a's and
|
136
150
|
# 'd's appear is the inverse of the order in which the corresponding operations are performed.
|
137
151
|
def accessor(sequence)
|
138
|
-
sequence.split(//).reverse!.
|
152
|
+
sequence.split(//).reverse!.inject(self) do |memo, char|
|
139
153
|
case char
|
140
154
|
when "a" then memo.head
|
141
155
|
when "d" then memo.tail
|
@@ -206,6 +220,7 @@ module Hamster
|
|
206
220
|
def map
|
207
221
|
self
|
208
222
|
end
|
223
|
+
alias_method :collect, :map
|
209
224
|
|
210
225
|
def reduce(memo)
|
211
226
|
memo
|
data/lib/hamster/set.rb
CHANGED
@@ -3,7 +3,7 @@ require 'hamster/trie'
|
|
3
3
|
module Hamster
|
4
4
|
|
5
5
|
def self.set(*items)
|
6
|
-
items.
|
6
|
+
items.inject(Set.new) { |set, item| set.add(item) }
|
7
7
|
end
|
8
8
|
|
9
9
|
class Set
|
@@ -45,13 +45,13 @@ module Hamster
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def each
|
48
|
-
block_given? or return
|
48
|
+
block_given? or return self
|
49
49
|
@trie.each { |entry| yield(entry.key) }
|
50
50
|
self
|
51
51
|
end
|
52
52
|
|
53
53
|
def map
|
54
|
-
block_given? or return
|
54
|
+
block_given? or return self
|
55
55
|
if empty?
|
56
56
|
self
|
57
57
|
else
|
@@ -67,7 +67,7 @@ module Hamster
|
|
67
67
|
alias_method :inject, :reduce
|
68
68
|
|
69
69
|
def filter
|
70
|
-
block_given? or return
|
70
|
+
block_given? or return self
|
71
71
|
trie = @trie.filter { |entry| yield(entry.key) }
|
72
72
|
if !trie.equal?(@trie)
|
73
73
|
self.class.new(trie)
|
@@ -78,7 +78,7 @@ module Hamster
|
|
78
78
|
alias_method :select, :filter
|
79
79
|
|
80
80
|
def reject
|
81
|
-
block_given? or return
|
81
|
+
block_given? or return self
|
82
82
|
select { |item| !yield(item) }
|
83
83
|
end
|
84
84
|
|
@@ -90,6 +90,8 @@ module Hamster
|
|
90
90
|
end
|
91
91
|
false
|
92
92
|
end
|
93
|
+
alias_method :exist?, :any?
|
94
|
+
alias_method :exists?, :any?
|
93
95
|
|
94
96
|
def all?
|
95
97
|
if block_given?
|
data/lib/hamster/stack.rb
CHANGED
@@ -3,12 +3,12 @@ require 'hamster/list'
|
|
3
3
|
module Hamster
|
4
4
|
|
5
5
|
def self.stack
|
6
|
-
|
6
|
+
EmptyStack
|
7
7
|
end
|
8
8
|
|
9
9
|
class Stack
|
10
10
|
|
11
|
-
def initialize(list
|
11
|
+
def initialize(list)
|
12
12
|
@list = list
|
13
13
|
end
|
14
14
|
|
@@ -19,6 +19,7 @@ module Hamster
|
|
19
19
|
def size
|
20
20
|
@list.size
|
21
21
|
end
|
22
|
+
alias_method :length, :size
|
22
23
|
|
23
24
|
def top
|
24
25
|
@list.head
|
@@ -30,15 +31,17 @@ module Hamster
|
|
30
31
|
|
31
32
|
def pop
|
32
33
|
list = @list.tail
|
33
|
-
if
|
34
|
-
|
34
|
+
if list.empty?
|
35
|
+
EmptyStack
|
35
36
|
else
|
36
|
-
self
|
37
|
+
self.class.new(list)
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
41
|
def eql?(other)
|
41
|
-
other.
|
42
|
+
return true if other.equal?(self)
|
43
|
+
return false unless other.class.equal?(self.class)
|
44
|
+
@list.eql?(other.instance_eval{@list})
|
42
45
|
end
|
43
46
|
alias_method :==, :eql?
|
44
47
|
|
@@ -49,4 +52,6 @@ module Hamster
|
|
49
52
|
|
50
53
|
end
|
51
54
|
|
55
|
+
EmptyStack = Stack.new(Hamster.list)
|
56
|
+
|
52
57
|
end
|
data/lib/hamster/version.rb
CHANGED
@@ -2,55 +2,59 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
|
2
2
|
|
3
3
|
describe Hamster::Hash do
|
4
4
|
|
5
|
-
|
5
|
+
[:any?, :exist?, :exists?].each do |method|
|
6
6
|
|
7
|
-
describe "
|
7
|
+
describe "##{method}" do
|
8
8
|
|
9
|
-
|
10
|
-
@hash = Hamster.hash
|
11
|
-
end
|
9
|
+
describe "when empty" do
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
before do
|
12
|
+
@hash = Hamster.hash
|
13
|
+
end
|
14
|
+
|
15
|
+
it "with a block returns false" do
|
16
|
+
@hash.send(method) {}.should be_false
|
17
|
+
end
|
18
|
+
|
19
|
+
it "with no block returns false" do
|
20
|
+
@hash.send(method).should be_false
|
21
|
+
end
|
16
22
|
|
17
|
-
it "with no block returns false" do
|
18
|
-
@hash.any?.should be_false
|
19
23
|
end
|
20
24
|
|
21
|
-
|
25
|
+
describe "when not empty" do
|
22
26
|
|
23
|
-
|
27
|
+
before do
|
28
|
+
@hash = Hamster.hash("A" => "aye", "B" => "bee", "C" => "see", nil => "NIL")
|
29
|
+
end
|
24
30
|
|
25
|
-
|
26
|
-
@hash = Hamster.hash("A" => "aye", "B" => "bee", "C" => "see", nil => "NIL")
|
27
|
-
end
|
31
|
+
describe "with a block" do
|
28
32
|
|
29
|
-
|
33
|
+
[
|
34
|
+
["A", "aye"],
|
35
|
+
["B", "bee"],
|
36
|
+
["C", "see"],
|
37
|
+
[nil, "NIL"],
|
38
|
+
].each do |pair|
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
["C", "see"],
|
35
|
-
[nil, "NIL"],
|
36
|
-
].each do |pair|
|
40
|
+
it "returns true if the block ever returns true (#{pair.inspect})" do
|
41
|
+
@hash.send(method) { |key, value| key == pair.first && value == pair.last }.should be_true
|
42
|
+
end
|
37
43
|
|
38
|
-
|
39
|
-
|
40
|
-
|
44
|
+
it "returns false if the block always returns false" do
|
45
|
+
@hash.send(method) { |key, value| key == "D" && value == "dee" }.should be_false
|
46
|
+
end
|
41
47
|
|
42
|
-
it "returns false if the block always returns false" do
|
43
|
-
@hash.any? { |key, value| key == "D" && value == "dee" }.should be_false
|
44
48
|
end
|
45
49
|
|
46
50
|
end
|
47
51
|
|
48
|
-
|
52
|
+
describe "with no block" do
|
49
53
|
|
50
|
-
|
54
|
+
it "returns true" do
|
55
|
+
@hash.send(method).should be_true
|
56
|
+
end
|
51
57
|
|
52
|
-
it "returns true" do
|
53
|
-
@hash.any?.should be_true
|
54
58
|
end
|
55
59
|
|
56
60
|
end
|
@@ -22,10 +22,10 @@ describe Hamster::Hash do
|
|
22
22
|
|
23
23
|
end
|
24
24
|
|
25
|
-
describe "with no block
|
25
|
+
describe "with no block" do
|
26
26
|
|
27
|
-
it "returns
|
28
|
-
|
27
|
+
it "returns self" do
|
28
|
+
@hash.each.should equal(@hash)
|
29
29
|
end
|
30
30
|
|
31
31
|
end
|
@@ -43,11 +43,11 @@ describe Hamster::Hash do
|
|
43
43
|
describe "with no block" do
|
44
44
|
|
45
45
|
before do
|
46
|
-
@
|
46
|
+
@result = @original.send(method)
|
47
47
|
end
|
48
48
|
|
49
|
-
it "returns
|
50
|
-
|
49
|
+
it "returns self" do
|
50
|
+
@result.should equal(@original)
|
51
51
|
end
|
52
52
|
|
53
53
|
end
|
@@ -44,15 +44,11 @@ describe Hamster::Hash do
|
|
44
44
|
describe "with no block" do
|
45
45
|
|
46
46
|
before do
|
47
|
-
@
|
47
|
+
@result = @original.send(method)
|
48
48
|
end
|
49
49
|
|
50
|
-
it "
|
51
|
-
@
|
52
|
-
end
|
53
|
-
|
54
|
-
it "returns an enumerator over the key value pairs" do
|
55
|
-
Hamster.hash(@enumerator.to_a).should == @original
|
50
|
+
it "returns self" do
|
51
|
+
@result.should equal(@original)
|
56
52
|
end
|
57
53
|
|
58
54
|
end
|
@@ -41,11 +41,11 @@ describe Hamster::Hash do
|
|
41
41
|
describe "with no block" do
|
42
42
|
|
43
43
|
before do
|
44
|
-
@
|
44
|
+
@result = @original.reject
|
45
45
|
end
|
46
46
|
|
47
|
-
it "returns
|
48
|
-
|
47
|
+
it "returns self" do
|
48
|
+
@result.should equal(@original)
|
49
49
|
end
|
50
50
|
|
51
51
|
end
|
@@ -38,6 +38,35 @@ describe Hamster do
|
|
38
38
|
|
39
39
|
end
|
40
40
|
|
41
|
+
describe ".stream" do
|
42
|
+
|
43
|
+
describe "with no block" do
|
44
|
+
|
45
|
+
before do
|
46
|
+
@stream = Hamster.stream
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns an empty list" do
|
50
|
+
@stream.should == Hamster.list
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "with a block" do
|
56
|
+
|
57
|
+
before do
|
58
|
+
count = 0
|
59
|
+
@stream = Hamster.stream { count += 1 }
|
60
|
+
end
|
61
|
+
|
62
|
+
it "repeatedly calls the block" do
|
63
|
+
@stream.take(5).should == Hamster.list(1, 2, 3, 4, 5)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
41
70
|
[:interval, :range].each do |method|
|
42
71
|
|
43
72
|
describe ".#{method}" do
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Hamster::List do
|
4
|
+
|
5
|
+
describe "#inspect" do
|
6
|
+
|
7
|
+
[
|
8
|
+
[],
|
9
|
+
["A"],
|
10
|
+
["A", "B", "C"],
|
11
|
+
].each do |values|
|
12
|
+
|
13
|
+
describe "on #{values.inspect}" do
|
14
|
+
|
15
|
+
list = Hamster.list(*values)
|
16
|
+
|
17
|
+
it "returns #{values.inspect}" do
|
18
|
+
list.inspect.should == values.inspect
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -2,36 +2,40 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
|
2
2
|
|
3
3
|
describe Hamster::List do
|
4
4
|
|
5
|
-
|
5
|
+
[:map, :collect].each do |method|
|
6
6
|
|
7
|
-
|
8
|
-
[[], []],
|
9
|
-
[["A"], ["a"]],
|
10
|
-
[["A", "B", "C"], ["a", "b", "c"]],
|
11
|
-
].each do |values, result|
|
7
|
+
describe "##{method}" do
|
12
8
|
|
13
|
-
|
9
|
+
[
|
10
|
+
[[], []],
|
11
|
+
[["A"], ["a"]],
|
12
|
+
[["A", "B", "C"], ["a", "b", "c"]],
|
13
|
+
].each do |values, result|
|
14
14
|
|
15
|
-
|
15
|
+
describe "on #{values.inspect}" do
|
16
16
|
|
17
|
-
|
17
|
+
list = Hamster.list(*values)
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
describe "with a block" do
|
20
|
+
|
21
|
+
it "returns #{result}" do
|
22
|
+
list.send(method) { |item| item.downcase }.should == Hamster.list(*result)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "is lazy" do
|
26
|
+
count = 0
|
27
|
+
list.send(method) { |item| count += 1 }
|
28
|
+
count.should <= 1
|
29
|
+
end
|
22
30
|
|
23
|
-
it "is lazy" do
|
24
|
-
count = 0
|
25
|
-
list.map { |item| count += 1 }
|
26
|
-
count.should <= 1
|
27
31
|
end
|
28
32
|
|
29
|
-
|
33
|
+
describe "without a block" do
|
30
34
|
|
31
|
-
|
35
|
+
it "returns self" do
|
36
|
+
list.send(method).should == list
|
37
|
+
end
|
32
38
|
|
33
|
-
it "returns self" do
|
34
|
-
list.map.should == list
|
35
39
|
end
|
36
40
|
|
37
41
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Hamster::List do
|
4
|
+
|
5
|
+
describe "#to_a" do
|
6
|
+
|
7
|
+
[
|
8
|
+
[],
|
9
|
+
["A"],
|
10
|
+
["A", "B", "C"],
|
11
|
+
].each do |values|
|
12
|
+
|
13
|
+
describe "on #{values.inspect}" do
|
14
|
+
|
15
|
+
list = Hamster.list(*values)
|
16
|
+
|
17
|
+
it "returns #{values.inspect}" do
|
18
|
+
list.to_a.should == values
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -2,54 +2,58 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
|
2
2
|
|
3
3
|
describe Hamster::Set do
|
4
4
|
|
5
|
-
|
5
|
+
[:any?, :exist?, :exists?].each do |method|
|
6
6
|
|
7
|
-
describe "
|
7
|
+
describe "##{method}" do
|
8
8
|
|
9
|
-
|
10
|
-
@set = Hamster.set
|
11
|
-
end
|
9
|
+
describe "when empty" do
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
before do
|
12
|
+
@set = Hamster.set
|
13
|
+
end
|
14
|
+
|
15
|
+
it "with a block returns false" do
|
16
|
+
@set.send(method) {}.should be_false
|
17
|
+
end
|
18
|
+
|
19
|
+
it "with no block returns false" do
|
20
|
+
@set.send(method).should be_false
|
21
|
+
end
|
16
22
|
|
17
|
-
it "with no block returns false" do
|
18
|
-
@set.any?.should be_false
|
19
23
|
end
|
20
24
|
|
21
|
-
|
25
|
+
describe "when not empty" do
|
22
26
|
|
23
|
-
|
27
|
+
describe "with a block" do
|
24
28
|
|
25
|
-
|
29
|
+
before do
|
30
|
+
@set = Hamster.set("A", "B", "C", nil)
|
31
|
+
end
|
26
32
|
|
27
|
-
|
28
|
-
@set = Hamster.set("A", "B", "C", nil)
|
29
|
-
end
|
33
|
+
["A", "B", "C", nil].each do |value|
|
30
34
|
|
31
|
-
|
35
|
+
it "returns true if the block ever returns true (#{value.inspect})" do
|
36
|
+
@set.send(method) { |item| item == value }.should be_true
|
37
|
+
end
|
32
38
|
|
33
|
-
it "returns true if the block ever returns true (#{value.inspect})" do
|
34
|
-
@set.any? { |item| item == value }.should be_true
|
35
39
|
end
|
36
40
|
|
37
|
-
|
41
|
+
it "returns false if the block always returns false" do
|
42
|
+
@set.send(method) { |item| item == "D" }.should be_false
|
43
|
+
end
|
38
44
|
|
39
|
-
it "returns false if the block always returns false" do
|
40
|
-
@set.any? { |item| item == "D" }.should be_false
|
41
45
|
end
|
42
46
|
|
43
|
-
|
47
|
+
describe "with no block" do
|
44
48
|
|
45
|
-
|
49
|
+
it "returns true if any value is truthy" do
|
50
|
+
Hamster.set(nil, false, true, "A").send(method).should be_true
|
51
|
+
end
|
46
52
|
|
47
|
-
|
48
|
-
|
49
|
-
|
53
|
+
it "returns false if all values are falsey" do
|
54
|
+
Hamster.set(nil, false).send(method).should be_false
|
55
|
+
end
|
50
56
|
|
51
|
-
it "returns false if all values are falsey" do
|
52
|
-
Hamster.set(nil, false).any?.should be_false
|
53
57
|
end
|
54
58
|
|
55
59
|
end
|
@@ -23,10 +23,10 @@ describe Hamster::Set do
|
|
23
23
|
|
24
24
|
end
|
25
25
|
|
26
|
-
describe "with no block
|
26
|
+
describe "with no block" do
|
27
27
|
|
28
|
-
it "returns
|
29
|
-
|
28
|
+
it "returns self" do
|
29
|
+
@set.each.should equal(@set)
|
30
30
|
end
|
31
31
|
|
32
32
|
end
|
@@ -43,11 +43,11 @@ describe Hamster::Set do
|
|
43
43
|
describe "with no block" do
|
44
44
|
|
45
45
|
before do
|
46
|
-
@
|
46
|
+
@result = @original.send(method)
|
47
47
|
end
|
48
48
|
|
49
|
-
it "returns
|
50
|
-
|
49
|
+
it "returns self" do
|
50
|
+
@result.should equal(@original)
|
51
51
|
end
|
52
52
|
|
53
53
|
end
|
@@ -44,15 +44,11 @@ describe Hamster::Set do
|
|
44
44
|
describe "with no block" do
|
45
45
|
|
46
46
|
before do
|
47
|
-
@
|
47
|
+
@result = @original.send(method)
|
48
48
|
end
|
49
49
|
|
50
|
-
it "
|
51
|
-
@
|
52
|
-
end
|
53
|
-
|
54
|
-
it "returns an enumerator over the values" do
|
55
|
-
Hamster.set(*@enumerator.to_a).should == @original
|
50
|
+
it "returns self" do
|
51
|
+
@result.should equal(@original)
|
56
52
|
end
|
57
53
|
|
58
54
|
end
|
@@ -41,11 +41,11 @@ describe Hamster::Set do
|
|
41
41
|
describe "with no block" do
|
42
42
|
|
43
43
|
before do
|
44
|
-
@
|
44
|
+
@result = @original.reject
|
45
45
|
end
|
46
46
|
|
47
|
-
it "returns
|
48
|
-
|
47
|
+
it "returns self" do
|
48
|
+
@result.should equal(@original)
|
49
49
|
end
|
50
50
|
|
51
51
|
end
|
@@ -6,18 +6,14 @@ describe Hamster::Stack do
|
|
6
6
|
@stack = Hamster.stack.push("A").push("B").push("C")
|
7
7
|
end
|
8
8
|
|
9
|
-
|
9
|
+
[:dup, :clone].each do |method|
|
10
10
|
|
11
|
-
|
12
|
-
@stack.dup.should equal(@stack)
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
11
|
+
describe "##{method}" do
|
16
12
|
|
17
|
-
|
13
|
+
it "returns self" do
|
14
|
+
@stack.send(method).should equal(@stack)
|
15
|
+
end
|
18
16
|
|
19
|
-
it "returns self" do
|
20
|
-
@stack.clone.should equal(@stack)
|
21
17
|
end
|
22
18
|
|
23
19
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hamster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Harris
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-12-
|
12
|
+
date: 2009-12-10 00:00:00 +11:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -59,7 +59,7 @@ files:
|
|
59
59
|
- spec/hamster/list/filter_spec.rb
|
60
60
|
- spec/hamster/list/head_spec.rb
|
61
61
|
- spec/hamster/list/include_spec.rb
|
62
|
-
- spec/hamster/list/
|
62
|
+
- spec/hamster/list/inspect_spec.rb
|
63
63
|
- spec/hamster/list/map_spec.rb
|
64
64
|
- spec/hamster/list/reduce_spec.rb
|
65
65
|
- spec/hamster/list/reject_spec.rb
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- spec/hamster/list/tail_spec.rb
|
68
68
|
- spec/hamster/list/take_spec.rb
|
69
69
|
- spec/hamster/list/take_while_spec.rb
|
70
|
+
- spec/hamster/list/to_a_spec.rb
|
70
71
|
- spec/hamster/set/add_spec.rb
|
71
72
|
- spec/hamster/set/all_spec.rb
|
72
73
|
- spec/hamster/set/any_spec.rb
|
@@ -108,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
109
|
requirements:
|
109
110
|
- - ">="
|
110
111
|
- !ruby/object:Gem::Version
|
111
|
-
version: 1.
|
112
|
+
version: 1.8.6
|
112
113
|
version:
|
113
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
115
|
requirements:
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
-
|
3
|
-
describe Hamster::List do
|
4
|
-
|
5
|
-
describe "laziness" do
|
6
|
-
|
7
|
-
def prime?(n)
|
8
|
-
2.upto(Math.sqrt(n).round) { |i| return false if n % i == 0 }
|
9
|
-
true
|
10
|
-
end
|
11
|
-
|
12
|
-
it "primes" do
|
13
|
-
numbers = Hamster.interval(10000, 1000000)
|
14
|
-
primes = numbers.filter { |i| prime?(i) }
|
15
|
-
primes.take(3)
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|