hamster 0.3.3 → 0.3.4

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.
@@ -1,3 +1,21 @@
1
+ === 0.3.4 / 2010-04-29
2
+
3
+ * Reduce garbage collection by keeping a singleton instance of an empty hash.
4
+
5
+ * Added some VERY experimental Read-Copy-Update collections (no tests).
6
+
7
+ * Slightly improve performance and greatly improve the readability of #eql?/#== methods.
8
+
9
+ * Added some VERY experimental memoization for immutable objects.
10
+
11
+ * Added Set#merge as an alias for Set#union.
12
+
13
+ * Implemented Hash#merge (aliased as #+).
14
+
15
+ === 0.3.3 / 2010-04-09
16
+
17
+ * Fixed: hash code generation had VERY poor distribution.
18
+
1
19
  === 0.3.1 / 2010-04-04
2
20
 
3
21
  * Fixed: Immutable not required in hamster.rb
@@ -6,7 +6,7 @@ require 'hamster/trie'
6
6
  module Hamster
7
7
 
8
8
  def self.hash(pairs = {})
9
- pairs.reduce(Hash.new) { |hash, pair| hash.put(pair.first, pair.last) }
9
+ pairs.reduce(EmptyHash) { |hash, pair| hash.put(pair.first, pair.last) }
10
10
  end
11
11
 
12
12
  class Hash
@@ -38,7 +38,7 @@ module Hamster
38
38
 
39
39
  def get(key)
40
40
  entry = @trie.get(key)
41
- return entry.value if entry
41
+ entry.value if entry
42
42
  end
43
43
  def_delegator :self, :get, :[]
44
44
 
@@ -114,12 +114,17 @@ module Hamster
114
114
  end
115
115
  def_delegator :self, :find, :detect
116
116
 
117
+ def merge(other)
118
+ transform { @trie = other.reduce(@trie, &:put) }
119
+ end
120
+ def_delegator :self, :merge, :+
121
+
117
122
  def keys
118
123
  reduce(Hamster.set) { |keys, key, value| keys.add(key) }
119
124
  end
120
125
 
121
126
  def eql?(other)
122
- other.is_a?(self.class) && @trie.eql?(other.instance_eval{@trie})
127
+ instance_of?(other.class) && @trie.eql?(other.instance_variable_get(:@trie))
123
128
  end
124
129
  def_delegator :self, :eql?, :==
125
130
 
@@ -137,4 +142,6 @@ module Hamster
137
142
 
138
143
  end
139
144
 
145
+ EmptyHash = Hash.new
146
+
140
147
  end
@@ -1,5 +1,3 @@
1
- require 'forwardable'
2
-
3
1
  module Hamster
4
2
 
5
3
  module Immutable
@@ -14,26 +12,57 @@ module Hamster
14
12
  module ClassMethods
15
13
 
16
14
  def new(*args)
17
- super.freeze
15
+ super.immutable!
16
+ end
17
+
18
+ def memoize(*names)
19
+ include MemoizeMethods unless include?(MemoizeMethods)
20
+ names.each do |name|
21
+ original_method = "__hamster_immutable_#{name}__"
22
+ alias_method original_method, name
23
+ class_eval <<-METHOD, __FILE__, __LINE__
24
+ def #{name}
25
+ if @__hamster_immutable_memory__.instance_variable_defined?(:@#{name})
26
+ @__hamster_immutable_memory__.instance_variable_get(:@#{name})
27
+ else
28
+ @__hamster_immutable_memory__.instance_variable_set(:@#{name}, #{original_method})
29
+ end
30
+ end
31
+ METHOD
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ module MemoizeMethods
38
+
39
+ def immutable!
40
+ @__hamster_immutable_memory__ = Object.new
41
+ freeze
18
42
  end
19
43
 
20
44
  end
21
45
 
22
46
  module InstanceMethods
23
47
 
24
- extend Forwardable
48
+ def immutable!
49
+ freeze
50
+ end
25
51
 
26
52
  def immutable?
27
53
  frozen?
28
54
  end
29
55
 
30
- alias_method :__copy__, :dup
31
- private :__copy__
56
+ alias_method :__hamster_immutable_dup__, :dup
57
+ private :__hamster_immutable_dup__
32
58
 
33
59
  def dup
34
60
  self
35
61
  end
36
- def_delegator :self, :dup, :clone
62
+
63
+ def clone
64
+ self
65
+ end
37
66
 
38
67
  protected
39
68
 
@@ -42,7 +71,7 @@ module Hamster
42
71
  end
43
72
 
44
73
  def transform(&block)
45
- __copy__.tap { |copy| copy.instance_eval(&block) }.freeze
74
+ __hamster_immutable_dup__.tap { |copy| copy.instance_eval(&block) }.immutable!
46
75
  end
47
76
 
48
77
  end
@@ -380,7 +380,7 @@ module Hamster
380
380
 
381
381
  def group_by(&block)
382
382
  return group_by { |item| item } unless block_given?
383
- reduce(Hamster::Hash.new) do |hash, item|
383
+ reduce(EmptyHash) do |hash, item|
384
384
  key = yield(item)
385
385
  hash.put(key, (hash.get(key) || EmptyList).cons(item))
386
386
  end
@@ -0,0 +1,46 @@
1
+ require 'forwardable'
2
+ require 'thread'
3
+
4
+ require 'hamster/hash'
5
+
6
+ module Hamster
7
+
8
+ class ReadCopyUpdateHash
9
+
10
+ extend Forwardable
11
+
12
+ def initialize
13
+ @hash = EmptyHash
14
+ @lock = Mutex.new
15
+ end
16
+
17
+ def put(key, value)
18
+ @lock.synchronize {
19
+ original_value = @hash.get(key)
20
+ @hash = @hash.put(key, value)
21
+ original_value
22
+ }
23
+ end
24
+
25
+ def delete(key)
26
+ @lock.synchronize {
27
+ original_value = @hash.get(key)
28
+ @hash = @hash.delete(key)
29
+ original_value
30
+ }
31
+ end
32
+
33
+ def eql?(other)
34
+ instance_of?(other.class) && @hash.eql?(other.instance_variable_get(:@hash))
35
+ end
36
+ def_delegator :self, :eql?, :==
37
+
38
+ private
39
+
40
+ def method_missing(name, *args, &block)
41
+ @hash.send(name, *args, &block)
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,40 @@
1
+ require 'forwardable'
2
+ require 'thread'
3
+
4
+ require 'hamster/set'
5
+
6
+ module Hamster
7
+
8
+ class ReadCopyUpdateSet
9
+
10
+ extend Forwardable
11
+
12
+ def initialize
13
+ @set = EmptySet
14
+ @lock = Mutex.new
15
+ end
16
+
17
+ def add(value)
18
+ @lock.synchronize { @set = @set.add(value) }
19
+ self
20
+ end
21
+
22
+ def delete(value)
23
+ @lock.synchronize { @set = @set.delete(value) }
24
+ self
25
+ end
26
+
27
+ def eql?(other)
28
+ instance_of?(other.class) && @set.eql?(other.instance_variable_get(:@set))
29
+ end
30
+ def_delegator :self, :eql?, :==
31
+
32
+ private
33
+
34
+ def method_missing(name, *args, &block)
35
+ @set.send(name, *args, &block)
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,43 @@
1
+ require 'forwardable'
2
+ require 'thread'
3
+
4
+ require 'hamster/set'
5
+
6
+ module Hamster
7
+
8
+ class ReadCopyUpdateStack
9
+
10
+ extend Forwardable
11
+
12
+ def initialize
13
+ @stack = EmptyStack
14
+ @lock = Mutex.new
15
+ end
16
+
17
+ def push(value)
18
+ @lock.synchronize { @stack = @stack.push(value) }
19
+ self
20
+ end
21
+
22
+ def pop
23
+ @lock.synchronize {
24
+ top_value = @stack.peek
25
+ @stack = @stack.pop
26
+ top_value
27
+ }
28
+ end
29
+
30
+ def eql?(other)
31
+ instance_of?(other.class) && @stack.eql?(other.instance_variable_get(:@stack))
32
+ end
33
+ def_delegator :self, :eql?, :==
34
+
35
+ private
36
+
37
+ def method_missing(name, *args, &block)
38
+ @stack.send(name, *args, &block)
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -191,6 +191,7 @@ module Hamster
191
191
  end
192
192
  def_delegator :self, :union, :|
193
193
  def_delegator :self, :union, :+
194
+ def_delegator :self, :union, :merge
194
195
 
195
196
  def intersection(other)
196
197
  trie = @trie.filter { |entry| other.include?(entry.key) }
@@ -233,7 +234,7 @@ module Hamster
233
234
 
234
235
  def group_by(&block)
235
236
  return group_by { |item| item } unless block_given?
236
- reduce(Hamster::Hash.new) do |hash, item|
237
+ reduce(EmptyHash) do |hash, item|
237
238
  key = yield(item)
238
239
  hash.put(key, (hash.get(key) || EmptySet).add(item))
239
240
  end
@@ -244,7 +245,7 @@ module Hamster
244
245
  end
245
246
 
246
247
  def eql?(other)
247
- other.is_a?(self.class) && @trie.eql?(other.instance_eval{@trie})
248
+ instance_of?(other.class) && @trie.eql?(other.instance_variable_get(:@trie))
248
249
  end
249
250
  def_delegator :self, :eql?, :==
250
251
 
@@ -51,9 +51,7 @@ module Hamster
51
51
  end
52
52
 
53
53
  def eql?(other)
54
- return true if other.equal?(self)
55
- return false unless other.class.equal?(self.class)
56
- @list.eql?(other.instance_eval{@list})
54
+ instance_of?(other.class) && @list.eql?(other.instance_variable_get(:@list))
57
55
  end
58
56
  def_delegator :self, :eql?, :==
59
57
 
@@ -91,8 +91,8 @@ module Hamster
91
91
 
92
92
  # Returns <tt>true</tt> if . <tt>eql?</tt> is synonymous with <tt>==</tt>
93
93
  def eql?(other)
94
- # return true if other.equal?(self)
95
- return false unless other.is_a?(self.class) && other.size == size
94
+ return true if equal?(other)
95
+ return false unless instance_of?(other.class) && size == other.size
96
96
  each do |entry|
97
97
  return false unless other.include?(entry.key, entry.value)
98
98
  end
@@ -24,8 +24,7 @@ module Hamster
24
24
 
25
25
  def eql?(other)
26
26
  return true if other.equal?(self)
27
- return false unless other.class.equal?(self.class)
28
- @items.eql?(other.instance_eval{@items})
27
+ instance_of?(other.class) && @items.eql?(other.instance_variable_get(:@items))
29
28
  end
30
29
  def_delegator :self, :eql?, :==
31
30
 
@@ -1,5 +1,5 @@
1
1
  module Hamster
2
2
 
3
- VERSION = "0.3.3".freeze
3
+ VERSION = "0.3.4".freeze
4
4
 
5
5
  end
@@ -0,0 +1,36 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/hash'
4
+
5
+ describe Hamster::Hash do
6
+
7
+ [:merge, :+].each do |method|
8
+
9
+ describe "##{method}" do
10
+
11
+ [
12
+ [[], [], []],
13
+ [["A" => "aye"], [], ["A" => "aye"]],
14
+ [["A" => "aye"], ["A" => "bee"], ["A" => "bee"]],
15
+ [["A" => "aye"], ["B" => "bee"], ["A" => "aye", "B" => "bee"]],
16
+ ].each do |a, b, expected|
17
+
18
+ describe "for #{a.inspect} and #{b.inspect}" do
19
+
20
+ before do
21
+ @result = Hamster.hash(*a).send(method, Hamster.hash(*b))
22
+ end
23
+
24
+ it "returns #{expected.inspect}" do
25
+ @result.should == Hamster.hash(*expected)
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+
5
+ describe Hamster::Immutable do
6
+
7
+ describe "#memoize" do
8
+
9
+ class Fixture
10
+ include Hamster::Immutable
11
+
12
+ def initialize(&block)
13
+ @block = block
14
+ end
15
+
16
+ def call
17
+ @block.call
18
+ end
19
+ memoize :call
20
+
21
+ def copy
22
+ transform {}
23
+ end
24
+
25
+ end
26
+
27
+ before do
28
+ @count = 0
29
+ @fixture = Fixture.new { @count += 1 }
30
+ @fixture.call
31
+ end
32
+
33
+ it "should still freezes be immutable" do
34
+ @fixture.should be_immutable
35
+ end
36
+
37
+ describe "when called multiple times" do
38
+
39
+ before do
40
+ @fixture.call
41
+ end
42
+
43
+ it "a memoized method will only be evaluated once" do
44
+ @count.should == 1
45
+ end
46
+
47
+ end
48
+
49
+ describe "when making a copy" do
50
+
51
+ before do
52
+ @copy = @fixture.copy
53
+ @copy.call
54
+ end
55
+
56
+ it "should clear all memory" do
57
+ @count.should == 2
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -4,7 +4,7 @@ require 'hamster/set'
4
4
 
5
5
  describe Hamster::Set do
6
6
 
7
- [:union, :|, :+].each do |method|
7
+ [:union, :|, :+, :merge].each do |method|
8
8
 
9
9
  describe "##{method}" do
10
10
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 3
8
- - 3
9
- version: 0.3.3
8
+ - 4
9
+ version: 0.3.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Simon Harris
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-09 00:00:00 +10:00
17
+ date: 2010-04-29 00:00:00 +10:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -62,6 +62,9 @@ files:
62
62
  - lib/hamster/hash.rb
63
63
  - lib/hamster/immutable.rb
64
64
  - lib/hamster/list.rb
65
+ - lib/hamster/read_copy_update_hash.rb
66
+ - lib/hamster/read_copy_update_set.rb
67
+ - lib/hamster/read_copy_update_stack.rb
65
68
  - lib/hamster/set.rb
66
69
  - lib/hamster/sorter.rb
67
70
  - lib/hamster/stack.rb
@@ -91,6 +94,7 @@ files:
91
94
  - spec/hamster/hash/inspect_spec.rb
92
95
  - spec/hamster/hash/keys_spec.rb
93
96
  - spec/hamster/hash/map_spec.rb
97
+ - spec/hamster/hash/merge_spec.rb
94
98
  - spec/hamster/hash/none_spec.rb
95
99
  - spec/hamster/hash/put_spec.rb
96
100
  - spec/hamster/hash/reduce_spec.rb
@@ -99,6 +103,7 @@ files:
99
103
  - spec/hamster/hash/uniq_spec.rb
100
104
  - spec/hamster/immutable/copying_spec.rb
101
105
  - spec/hamster/immutable/immutable_spec.rb
106
+ - spec/hamster/immutable/memoize_spec.rb
102
107
  - spec/hamster/immutable/new_spec.rb
103
108
  - spec/hamster/immutable/transform_spec.rb
104
109
  - spec/hamster/immutable/transform_unless_spec.rb