redshift 1.3.15 → 1.3.16
Sign up to get free protection for your applications and to get access to all the features.
- data/RELEASE-NOTES +4 -0
- data/bench/diff-bench +2 -2
- data/bench/run +1 -1
- data/bench/strictness.rb +2 -2
- data/examples/ball.rb +1 -1
- data/examples/collide.rb +1 -1
- data/examples/delay.rb +1 -1
- data/examples/derivative.rb +1 -1
- data/examples/euler.rb +1 -1
- data/examples/lotka-volterra.rb +1 -1
- data/examples/pid.rb +1 -1
- data/examples/subsystem.rb +1 -1
- data/examples/thermostat.rb +1 -1
- data/lib/redshift/component.rb +2 -2
- data/lib/redshift/redshift.rb +2 -2
- data/lib/{accessible-index.rb → redshift/util/accessible-index.rb} +0 -0
- data/lib/redshift/util/argos.rb +214 -0
- data/lib/redshift/util/histogram.rb +155 -0
- data/lib/redshift/util/object-diff.rb +83 -0
- data/lib/redshift/util/plot.rb +386 -0
- data/lib/redshift/util/random.rb +261 -0
- data/lib/redshift/util/superhash.rb +454 -0
- data/lib/redshift/util/tracer.rb +113 -0
- data/lib/redshift/util/tracer/trace.rb +145 -0
- data/lib/redshift/util/tracer/var.rb +112 -0
- data/rakefile +2 -2
- data/test/test_flow_trans.rb +28 -25
- metadata +45 -7
- data/examples/persist-ball.rb +0 -68
@@ -0,0 +1,261 @@
|
|
1
|
+
module Random
|
2
|
+
|
3
|
+
# Base class for sequences that sample different kinds of distributions.
|
4
|
+
# The actual PRNG must be plugged in at initialization, or else ruby's
|
5
|
+
# global PRNG is used.
|
6
|
+
class Sequence
|
7
|
+
include Math
|
8
|
+
|
9
|
+
class RubyGlobalGenerator
|
10
|
+
attr_reader :seed
|
11
|
+
|
12
|
+
def initialize(seed = nil)
|
13
|
+
@seed = seed
|
14
|
+
srand(seed) if seed
|
15
|
+
end
|
16
|
+
def next
|
17
|
+
rand
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :generator
|
22
|
+
|
23
|
+
# Options are :seed and :generator.
|
24
|
+
#
|
25
|
+
# The :generator must either have a method #next that returns
|
26
|
+
# a float between 0 and 1, or a method #new that returns an instance
|
27
|
+
# that has such a #next method.
|
28
|
+
#
|
29
|
+
# If generator is not given, uses ruby's Kernel#rand (beware global state)
|
30
|
+
# and the :seed option.
|
31
|
+
#
|
32
|
+
def initialize opt = {}
|
33
|
+
gen = opt[:generator] || RubyGlobalGenerator
|
34
|
+
if gen.respond_to?(:new)
|
35
|
+
@generator = gen.new(opt[:seed])
|
36
|
+
else
|
37
|
+
@generator = gen
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.serial_count
|
42
|
+
@count ||= 0
|
43
|
+
@count += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
# A utility method for getting a random seed.
|
47
|
+
def self.random_seed
|
48
|
+
Sequence.random_pool_seed ||
|
49
|
+
((Time.now.to_f * 1_000_000_000).to_i % 1_000_000_000) +
|
50
|
+
Sequence.serial_count + Process.pid
|
51
|
+
end
|
52
|
+
|
53
|
+
@@have_dev_random = true # assume so until evidence to contrary
|
54
|
+
|
55
|
+
def self.random_pool_seed
|
56
|
+
## could also get random data from net
|
57
|
+
if @@have_dev_random
|
58
|
+
@random_pool ||= ""
|
59
|
+
if @random_pool.length < 4
|
60
|
+
File.open('/dev/random') do |dr|
|
61
|
+
if select([dr],nil,nil,0)
|
62
|
+
@random_pool << dr.sysread(100)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if @random_pool.length >= 4
|
67
|
+
@random_pool.slice!(-4..-1).unpack('L')[0]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
rescue SystemCallError
|
71
|
+
@@have_dev_random = false
|
72
|
+
end
|
73
|
+
|
74
|
+
def next
|
75
|
+
@generator.next
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ConstantSequence < Sequence
|
80
|
+
attr_reader :mean
|
81
|
+
|
82
|
+
def initialize opt = {}
|
83
|
+
@mean = Float(opt[:mean] || 0)
|
84
|
+
end
|
85
|
+
|
86
|
+
def next
|
87
|
+
@mean
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class UniformSequence < Sequence
|
92
|
+
attr_reader :min, :max
|
93
|
+
|
94
|
+
def initialize opt = {}
|
95
|
+
super
|
96
|
+
@min = Float(opt[:min] || 0)
|
97
|
+
@max = Float(opt[:max] || 1)
|
98
|
+
@delta = @max - @min
|
99
|
+
end
|
100
|
+
|
101
|
+
def next
|
102
|
+
@min + @delta*super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class ExponentialSequence < Sequence
|
107
|
+
attr_reader :mean
|
108
|
+
|
109
|
+
def initialize opt = {}
|
110
|
+
super
|
111
|
+
@mean = Float(opt[:mean] || 1)
|
112
|
+
end
|
113
|
+
|
114
|
+
def next
|
115
|
+
while (x=super) == 0.0; end
|
116
|
+
return -log(x) * @mean
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class GaussianSequence < Sequence
|
121
|
+
attr_reader :mean, :stdev, :min, :max
|
122
|
+
|
123
|
+
def initialize opt = {}
|
124
|
+
super
|
125
|
+
@mean = Float(opt[:mean] || 0)
|
126
|
+
@stdev = Float(opt[:stdev] || 1)
|
127
|
+
@min = opt[:min]; @min = Float(@min) if @min
|
128
|
+
@max = opt[:max]; @max = Float(@max) if @max
|
129
|
+
@nextnext = nil
|
130
|
+
end
|
131
|
+
|
132
|
+
def next
|
133
|
+
if @nextnext
|
134
|
+
result = @mean + @nextnext*@stdev
|
135
|
+
@nextnext = nil
|
136
|
+
|
137
|
+
else
|
138
|
+
begin
|
139
|
+
v1 = 2 * super - 1
|
140
|
+
v2 = 2 * super - 1
|
141
|
+
rsq = v1*v1 + v2*v2
|
142
|
+
end while rsq >= 1 || rsq == 0
|
143
|
+
|
144
|
+
fac = sqrt(-2*log(rsq) / rsq)
|
145
|
+
@nextnext = v1*fac
|
146
|
+
result = @mean + v2*fac*@stdev
|
147
|
+
end
|
148
|
+
|
149
|
+
if @min and result < @min
|
150
|
+
result = @min
|
151
|
+
elsif @max and result > @max
|
152
|
+
result = @max
|
153
|
+
end
|
154
|
+
|
155
|
+
return result
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Based on newran02:
|
160
|
+
#
|
161
|
+
# Real VariLogNormal::Next(Real mean, Real sd)
|
162
|
+
# {
|
163
|
+
# // should have special version of log for small sd/mean
|
164
|
+
# Real n_var = log(1 + square(sd / mean));
|
165
|
+
# return mean * exp(N.Next() * sqrt(n_var) - 0.5 * n_var);
|
166
|
+
# }
|
167
|
+
#
|
168
|
+
class LogNormalSequence < Sequence
|
169
|
+
attr_reader :mean, :stdev
|
170
|
+
|
171
|
+
def initialize opt = {}
|
172
|
+
@gaussian_seq = GaussianSequence.new(
|
173
|
+
:mean => 0,
|
174
|
+
:stdev => 1,
|
175
|
+
:seed => opt[:seed],
|
176
|
+
:generator => opt[:generator]
|
177
|
+
)
|
178
|
+
|
179
|
+
super :generator => @gaussian_seq
|
180
|
+
|
181
|
+
@mean = Float(opt[:mean] || 1)
|
182
|
+
@stdev = Float(opt[:stdev] || 1)
|
183
|
+
|
184
|
+
n_var = log(1 + (stdev / mean)**2)
|
185
|
+
@sqrt_n_var = sqrt(n_var)
|
186
|
+
@half_n_var = 0.5 * n_var
|
187
|
+
end
|
188
|
+
|
189
|
+
def next
|
190
|
+
mean * exp(super() * @sqrt_n_var - @half_n_var)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
class DiscreteSequence < Sequence
|
195
|
+
attr_reader :distrib
|
196
|
+
|
197
|
+
def initialize opt = {}
|
198
|
+
super
|
199
|
+
@distrib = opt[:distrib] || { 0 => 1.0 }
|
200
|
+
|
201
|
+
sum = @distrib.inject(0) {|sum, (pt, prob)| sum + prob}
|
202
|
+
sum = sum.to_f # so division is ok
|
203
|
+
|
204
|
+
@distrib.keys.each do |point|
|
205
|
+
@distrib[point] /= sum
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def next
|
210
|
+
loop do
|
211
|
+
r = super
|
212
|
+
@distrib.each do |point, probability|
|
213
|
+
if r < probability
|
214
|
+
return point
|
215
|
+
end
|
216
|
+
r -= probability
|
217
|
+
end
|
218
|
+
# repeat if failed to get a result (due to floating point imprecision)
|
219
|
+
end
|
220
|
+
## this would be faster using an rbtree
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
Constant = ConstantSequence
|
225
|
+
Uniform = UniformSequence
|
226
|
+
Exponential = ExponentialSequence
|
227
|
+
Gaussian = GaussianSequence
|
228
|
+
Normal = NormalSequence = GaussianSequence
|
229
|
+
LogNormal = LogNormalSequence
|
230
|
+
Discrete = DiscreteSequence
|
231
|
+
end
|
232
|
+
|
233
|
+
if __FILE__ == $0
|
234
|
+
require 'redshift/util/argos'
|
235
|
+
|
236
|
+
defaults = {
|
237
|
+
"n" => 10,
|
238
|
+
"d" => Random::Uniform
|
239
|
+
}
|
240
|
+
|
241
|
+
optdef = {
|
242
|
+
"n" => proc {|n| Integer(n)},
|
243
|
+
"d" => proc {|d|
|
244
|
+
Random.const_get(Random.constants.grep(/^#{d}/i).first)
|
245
|
+
},
|
246
|
+
"m" => proc {|m| Float(m)},
|
247
|
+
"s" => proc {|s| Float(s)},
|
248
|
+
"seed" => proc {|seed| Integer(seed)},
|
249
|
+
}
|
250
|
+
|
251
|
+
begin
|
252
|
+
opts = defaults.merge(Argos.parse_options(ARGV, optdef))
|
253
|
+
rescue Argos::OptionError => ex
|
254
|
+
$stderr.puts ex.message
|
255
|
+
exit
|
256
|
+
end
|
257
|
+
|
258
|
+
seq = opts["d"].new :mean => opts["m"], :stdev => opts["s"],
|
259
|
+
:seed => opts["seed"]
|
260
|
+
puts (0...opts["n"]).map {seq.next}
|
261
|
+
end
|
@@ -0,0 +1,454 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
==class SuperHash
|
4
|
+
|
5
|
+
The Ruby inheritance system is a powerful way to organize methods and constants
|
6
|
+
in a hierarchy of classes and modules. However, it does not provide an easy way
|
7
|
+
to organize class attributes with inherited values in such a hierarchy. There is no inheritance mechanism that combines:
|
8
|
+
|
9
|
+
1. propagation of values to descendant classes;
|
10
|
+
|
11
|
+
2. overriding of values by a subclass; and
|
12
|
+
|
13
|
+
3. mutability.
|
14
|
+
|
15
|
+
The closest approximations in Ruby are class variables, class instance variables, and constants.
|
16
|
+
|
17
|
+
A class variable ((({@@var}))) is stored in the base class in which it was
|
18
|
+
defined. When its value is changed by a subclass, the change propagates to all
|
19
|
+
subclasses of the base class. The value cannot be overridden just for that
|
20
|
+
subclass and its descendants. This satisfies 1 and 3, but not 2.
|
21
|
+
|
22
|
+
A class instance variable ((({@var}))) can take on a different value in each
|
23
|
+
subclass, but there is no inheritance mechanism. Its value is privately
|
24
|
+
accessible by its owner (though it may be exposed by methods). However, the value does not propagate to subclasses. This satisfies 2 and 3, but not 1.
|
25
|
+
|
26
|
+
A constant is inherited and can take on different values in subclasses. However it cannot be changed and is always public. This satisfies 1 and 2, but not 3.
|
27
|
+
|
28
|
+
(({SuperHash})) solves this class attribute problem and in addition is a
|
29
|
+
general mechanism for defining attribute inheritance structures among objects
|
30
|
+
of any type, not just classes. An example of the former is (({StateObject})),
|
31
|
+
in (({examples/state-object.rb})). An example of the latter is
|
32
|
+
(({AttributedNode})), in (({examples/attributed-node.rb})).
|
33
|
+
|
34
|
+
A superhash is simply a hash bundled with a list of parents, which can be
|
35
|
+
hashes or other hash-like objects. For all lookup methods, like (({[]})),
|
36
|
+
(({each})), (({size})), and so on, the superhash behaves as if the parent hash
|
37
|
+
entries were included in it. The inheritance search is depth-first, and in the
|
38
|
+
same order as the parents list.
|
39
|
+
|
40
|
+
Destructive methods, such as (({[]=})) and (({delete})), do not affect the
|
41
|
+
parent (however, see (({rehash})) below), but attempt to emulate the expected
|
42
|
+
effect by changing the superhash itself. Operations on a parent are immdiately
|
43
|
+
reflected in the child; the parent's data is referenced, not copied, by the
|
44
|
+
child.
|
45
|
+
|
46
|
+
The equality semantics of (({SuperHash})) is the same as that of (({Hash})).
|
47
|
+
The (({==})) method returns true if and only if the receiver and the argument
|
48
|
+
have the same (in the sense of (({==}))) key-value pairs. The (({eql?}))
|
49
|
+
method is inherited from (({Object})). Naturally, (({SuperHash})) includes the
|
50
|
+
(({Enumerable})) module.
|
51
|
+
|
52
|
+
Note that (({SuperHash})) is not very efficient. Because (({SuperHash})) is
|
53
|
+
dynamic and flexible, even an operation as simple as (({size})) requires
|
54
|
+
sending (({size})) messages to the parents. Also, the current implementation
|
55
|
+
emphasizes simplicity over speed. For instance, (({each})) requires
|
56
|
+
constructing the set of all keys, which requires collecting key sets for
|
57
|
+
parents, and then taking their union.
|
58
|
+
|
59
|
+
===class method
|
60
|
+
|
61
|
+
---SuperHash.new parents = [], default = nil
|
62
|
+
|
63
|
+
The (({parents})) argument can be an enumerable collection of hash-like
|
64
|
+
objects, or a single hash-like object, or [] or nil. The hash-like objects must
|
65
|
+
support (({find})), (({collect})), (({keys})), (({key?})), and (({[]})).
|
66
|
+
|
67
|
+
The precedence order of parents is the same as their order in the (({parents}))
|
68
|
+
array. In other words, the first parent in the list overrides later ones, and
|
69
|
+
so on. Inheritance is by depth first.
|
70
|
+
|
71
|
+
If the (({default})) argument is specified, it affects the (({SuperHash})) just
|
72
|
+
like the (({default})) argument in the (({Hash})) constructor. The default
|
73
|
+
behavior of the child replaces the default behaviors of the parents.
|
74
|
+
|
75
|
+
===overridden instance methods
|
76
|
+
|
77
|
+
The SuperHash instance methods provide a hash-like interface. Hash methods which
|
78
|
+
need special explanation are documented below.
|
79
|
+
|
80
|
+
---SuperHash#clear
|
81
|
+
|
82
|
+
The implementation of (({clear})) is to simply call (({delete_if {true}})).
|
83
|
+
|
84
|
+
---SuperHash#delete(key)
|
85
|
+
---SuperHash#delete(key) { |key| block }
|
86
|
+
---SuperHash#delete_if { |key, value| block }
|
87
|
+
|
88
|
+
If the key is inherited, these methods simply associate the default value to
|
89
|
+
the key in the (({SuperHash})). Note that if the default is changed after the
|
90
|
+
deletion, the key-value pair is not updated to reflect the change--the value
|
91
|
+
will still be the old default.
|
92
|
+
|
93
|
+
---SuperHash#empty?
|
94
|
+
---SuperHash#size
|
95
|
+
|
96
|
+
Note that (({superhash.clear.empty?})) will not return (({true})) if there are
|
97
|
+
inherited keys. The (({SuperHash})) needs to remember which parent keys have
|
98
|
+
been deleted, and this is not easily distinguishable from the case in which
|
99
|
+
those keys have been explicitly associated with (({nil})) (or the default
|
100
|
+
value). Similar remarks apply to (({size})).
|
101
|
+
|
102
|
+
---SuperHash#invert
|
103
|
+
---SuperHash#to_hash
|
104
|
+
|
105
|
+
Returns a (({Hash})), in the first case with inverted key-value pairs, in the
|
106
|
+
second case with the same key-value pairs, as the receiver.
|
107
|
+
|
108
|
+
---SuperHash#rehash
|
109
|
+
|
110
|
+
Rehashes the receiver's (({own})) hash and rehashes all parents (if they
|
111
|
+
respond to (({rehash}))). Note that this is the only (({SuperHash})) method
|
112
|
+
that modifies the parent objects.
|
113
|
+
|
114
|
+
---SuperHash#replace(hash)
|
115
|
+
|
116
|
+
Replaces the receiver's (({own})) hash with the argument, and replaces the
|
117
|
+
receiver's parent array with the empty array.
|
118
|
+
|
119
|
+
---SuperHash#shift
|
120
|
+
|
121
|
+
As long as the (({own})) hash has entries, shifts them out and returns them.
|
122
|
+
Raises (({ParentImmutableError})) if the receiver's (({own})) hash is empty.
|
123
|
+
|
124
|
+
===new instance methods
|
125
|
+
|
126
|
+
(({SuperHash})) defines some instance methods that are not available in
|
127
|
+
(({Hash})).
|
128
|
+
|
129
|
+
---SuperHash#inherits_key? k
|
130
|
+
|
131
|
+
Returns (({true})) if and only if (({k})) is a key in a parent but not in the
|
132
|
+
receiver's (({own})) hash.
|
133
|
+
|
134
|
+
---SuperHash#own
|
135
|
+
|
136
|
+
Returns the hash of key-value pairs that belong to the superhash and are not
|
137
|
+
inherited.
|
138
|
+
|
139
|
+
---SuperHash#own_keys
|
140
|
+
|
141
|
+
Returns the array of keys in the (({own})) hash.
|
142
|
+
|
143
|
+
---SuperHash#owns_key? k
|
144
|
+
|
145
|
+
Returns (({true})) if and only if (({k})) is a key in the (({own})) hash.
|
146
|
+
|
147
|
+
==version
|
148
|
+
|
149
|
+
SuperHash 0.3
|
150
|
+
|
151
|
+
The current version of this software can be found at
|
152
|
+
((<"http://redshift.sourceforge.net/superhash
|
153
|
+
"|URL:http://redshift.sourceforge.net/superhash>)).
|
154
|
+
|
155
|
+
==license
|
156
|
+
This software is distributed under the Ruby license.
|
157
|
+
See ((<"http://www.ruby-lang.org"|URL:http://www.ruby-lang.org>)).
|
158
|
+
|
159
|
+
==author
|
160
|
+
Joel VanderWerf,
|
161
|
+
((<vjoel@users.sourceforge.net|URL:mailto:vjoel@users.sourceforge.net>))
|
162
|
+
|
163
|
+
=end
|
164
|
+
|
165
|
+
class SuperHash
|
166
|
+
include Enumerable
|
167
|
+
|
168
|
+
attr_reader :parents
|
169
|
+
|
170
|
+
def initialize parents = [], default = nil
|
171
|
+
@hash = Hash.new default
|
172
|
+
if parents == nil
|
173
|
+
@parents = []
|
174
|
+
elsif parents.respond_to? :key?
|
175
|
+
@parents = [parents]
|
176
|
+
else
|
177
|
+
@parents = parents
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# methods that are not overrides of Hash methods
|
182
|
+
|
183
|
+
def inherits_key? k
|
184
|
+
!(@hash.key? k) && (!! @parents.find {|parent| parent.key? k } )
|
185
|
+
end
|
186
|
+
|
187
|
+
def own
|
188
|
+
@hash
|
189
|
+
end
|
190
|
+
|
191
|
+
def own_keys
|
192
|
+
@hash.keys
|
193
|
+
end
|
194
|
+
|
195
|
+
def owns_key? k
|
196
|
+
@hash.key? k
|
197
|
+
end
|
198
|
+
|
199
|
+
# methods that override Hash methods
|
200
|
+
|
201
|
+
def ==(other)
|
202
|
+
return false unless other.respond_to? :size and
|
203
|
+
size == other.size and
|
204
|
+
other.respond_to? :[]
|
205
|
+
each { |key, value| return false unless self[key] == other[key] }
|
206
|
+
return true
|
207
|
+
end
|
208
|
+
|
209
|
+
def [](key)
|
210
|
+
fetch(key) {default}
|
211
|
+
end
|
212
|
+
|
213
|
+
def []=(key, value)
|
214
|
+
@hash[key] = value
|
215
|
+
end
|
216
|
+
alias store []=
|
217
|
+
|
218
|
+
def clear
|
219
|
+
delete_if {true}
|
220
|
+
end
|
221
|
+
|
222
|
+
def default
|
223
|
+
@hash.default
|
224
|
+
end
|
225
|
+
|
226
|
+
def default=(value)
|
227
|
+
@hash.default = value
|
228
|
+
end
|
229
|
+
|
230
|
+
def delete(key)
|
231
|
+
if key? key
|
232
|
+
@hash.delete(key) do
|
233
|
+
value = fetch(key)
|
234
|
+
@hash[key] = default
|
235
|
+
value
|
236
|
+
end
|
237
|
+
else
|
238
|
+
block_given? ? (yield key) : default
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def delete_if
|
243
|
+
each do |key, value|
|
244
|
+
if yield key, value
|
245
|
+
@hash.delete(key) { @hash[key] = default }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def each
|
251
|
+
keys.each { |k| yield k, fetch(k) }
|
252
|
+
self
|
253
|
+
end
|
254
|
+
alias each_pair each
|
255
|
+
|
256
|
+
def each_key
|
257
|
+
keys.each { |k| yield k }
|
258
|
+
self
|
259
|
+
end
|
260
|
+
|
261
|
+
def each_value
|
262
|
+
keys.each { |k| yield fetch(k) }
|
263
|
+
self
|
264
|
+
end
|
265
|
+
|
266
|
+
def empty?
|
267
|
+
@hash.empty? && ( not @parents.find {|parent| not parent.empty?} )
|
268
|
+
end
|
269
|
+
|
270
|
+
def fetch(*args)
|
271
|
+
case args.size
|
272
|
+
when 1
|
273
|
+
key, = args
|
274
|
+
@hash.fetch(key) {
|
275
|
+
@parents.each do |parent|
|
276
|
+
begin
|
277
|
+
return parent.fetch(key)
|
278
|
+
rescue IndexError
|
279
|
+
end
|
280
|
+
end
|
281
|
+
if block_given?
|
282
|
+
yield key
|
283
|
+
else
|
284
|
+
raise IndexError, "key not found"
|
285
|
+
end
|
286
|
+
}
|
287
|
+
when 2
|
288
|
+
if block_given?
|
289
|
+
raise ArgumentError, "wrong # of arguments"
|
290
|
+
end
|
291
|
+
key, default_object = args
|
292
|
+
@hash.fetch(key) {
|
293
|
+
@parents.each do |parent|
|
294
|
+
begin
|
295
|
+
return parent.fetch(key)
|
296
|
+
rescue IndexError
|
297
|
+
end
|
298
|
+
end
|
299
|
+
return default_object
|
300
|
+
}
|
301
|
+
else
|
302
|
+
raise ArgumentError, "wrong # of arguments(#{args.size} for 2)"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def has_value? val
|
307
|
+
each { |k,v| return true if val == v }
|
308
|
+
return false
|
309
|
+
end
|
310
|
+
alias value? has_value?
|
311
|
+
|
312
|
+
def index val
|
313
|
+
each { |k,v| return k if val == v }
|
314
|
+
return false
|
315
|
+
end
|
316
|
+
|
317
|
+
def indexes(*ks)
|
318
|
+
ks.collect { |k| index k }
|
319
|
+
end
|
320
|
+
alias indices indexes
|
321
|
+
|
322
|
+
def invert
|
323
|
+
h = {}
|
324
|
+
keys.each { |k| h[fetch(k)] = k }
|
325
|
+
h
|
326
|
+
end
|
327
|
+
|
328
|
+
def key? k
|
329
|
+
(@hash.key? k) || (!! @parents.find {|parent| parent.key?(k)} )
|
330
|
+
end
|
331
|
+
alias has_key? key?
|
332
|
+
alias include? key?
|
333
|
+
alias member? key?
|
334
|
+
|
335
|
+
def keys
|
336
|
+
(@hash.keys + (@parents.collect { |parent| parent.keys }).flatten).uniq
|
337
|
+
end
|
338
|
+
|
339
|
+
def rehash
|
340
|
+
@hash.rehash
|
341
|
+
@parents.each { |parent| parent.rehash if parent.respond_to? :rehash }
|
342
|
+
self
|
343
|
+
end
|
344
|
+
|
345
|
+
def reject
|
346
|
+
dup.delete_if { |k, v| yield k, v } ## or is '&Proc.new' faster?
|
347
|
+
end
|
348
|
+
|
349
|
+
def reject!
|
350
|
+
changed = false
|
351
|
+
|
352
|
+
each do |key, value|
|
353
|
+
if yield key, value
|
354
|
+
changed = true
|
355
|
+
@hash.delete(key) { @hash[key] = default }
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
changed ? self : nil
|
360
|
+
end
|
361
|
+
|
362
|
+
def replace hash
|
363
|
+
@hash.replace hash
|
364
|
+
@parents.replace []
|
365
|
+
end
|
366
|
+
|
367
|
+
class ParentImmutableError < StandardError; end
|
368
|
+
|
369
|
+
def shift
|
370
|
+
if @hash.empty?
|
371
|
+
raise ParentImmutableError, "Attempted to shift data out of parent"
|
372
|
+
else
|
373
|
+
@hash.shift
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
def size
|
378
|
+
keys.size
|
379
|
+
end
|
380
|
+
alias length size
|
381
|
+
|
382
|
+
def sort
|
383
|
+
if block_given?
|
384
|
+
to_a.sort { |x, y| yield x, y } ## or is '&Proc.new' faster?
|
385
|
+
else
|
386
|
+
to_a.sort
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def to_a
|
391
|
+
to_hash.to_a
|
392
|
+
end
|
393
|
+
|
394
|
+
def to_hash
|
395
|
+
h = {}
|
396
|
+
keys.each { |k| h[k] = fetch(k) }
|
397
|
+
h
|
398
|
+
end
|
399
|
+
|
400
|
+
def to_s
|
401
|
+
to_hash.to_s
|
402
|
+
end
|
403
|
+
|
404
|
+
def update h
|
405
|
+
@hash.update h
|
406
|
+
self
|
407
|
+
end
|
408
|
+
|
409
|
+
def values
|
410
|
+
keys.collect { |k| self[k] }
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|
414
|
+
|
415
|
+
class Class
|
416
|
+
private
|
417
|
+
def class_superhash(*vars)
|
418
|
+
for var in vars
|
419
|
+
class_eval %{
|
420
|
+
@#{var} = Hash.new
|
421
|
+
def self.#{var}
|
422
|
+
@#{var} ||= SuperHash.new(superclass.#{var})
|
423
|
+
end
|
424
|
+
}
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
# A superhash of key-value pairs in which the value is a superhash
|
429
|
+
# which inherits from the key-indexed superhash in the superclass.
|
430
|
+
def class_superhash2(*vars)
|
431
|
+
for var in vars
|
432
|
+
class_eval %{
|
433
|
+
@#{var} = Hash.new
|
434
|
+
def self.#{var}(arg = nil)
|
435
|
+
@#{var} ||= SuperHash.new(superclass.#{var})
|
436
|
+
if arg
|
437
|
+
if self == #{self.name}
|
438
|
+
unless @#{var}.has_key? arg
|
439
|
+
@#{var}[arg] = Hash.new
|
440
|
+
end
|
441
|
+
else
|
442
|
+
unless @#{var}.owns_key? arg
|
443
|
+
@#{var}[arg] = SuperHash.new(superclass.#{var}(arg))
|
444
|
+
end
|
445
|
+
end
|
446
|
+
@#{var}[arg]
|
447
|
+
else
|
448
|
+
@#{var}
|
449
|
+
end
|
450
|
+
end
|
451
|
+
}
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|