spruz 0.1.0
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.
- data/README +0 -0
- data/Rakefile +85 -0
- data/VERSION +1 -0
- data/bin/enum +167 -0
- data/install.rb +33 -0
- data/lib/spruz.rb +22 -0
- data/lib/spruz/bijection.rb +31 -0
- data/lib/spruz/count_by.rb +8 -0
- data/lib/spruz/generator.rb +66 -0
- data/lib/spruz/go.rb +37 -0
- data/lib/spruz/hash_union.rb +8 -0
- data/lib/spruz/limited.rb +36 -0
- data/lib/spruz/memoize.rb +71 -0
- data/lib/spruz/minimize.rb +53 -0
- data/lib/spruz/module_group.rb +11 -0
- data/lib/spruz/null.rb +20 -0
- data/lib/spruz/once.rb +23 -0
- data/lib/spruz/p.rb +21 -0
- data/lib/spruz/partial_application.rb +29 -0
- data/lib/spruz/round.rb +38 -0
- data/lib/spruz/shuffle.rb +15 -0
- data/lib/spruz/subhash.rb +37 -0
- data/lib/spruz/time_dummy.rb +29 -0
- data/lib/spruz/to_proc.rb +9 -0
- data/lib/spruz/uniq_by.rb +8 -0
- data/lib/spruz/version.rb +8 -0
- data/lib/spruz/xt.rb +15 -0
- data/lib/spruz/xt/blank.rb +67 -0
- data/lib/spruz/xt/count_by.rb +11 -0
- data/lib/spruz/xt/full.rb +33 -0
- data/lib/spruz/xt/hash_union.rb +11 -0
- data/lib/spruz/xt/irb.rb +17 -0
- data/lib/spruz/xt/null.rb +5 -0
- data/lib/spruz/xt/p.rb +7 -0
- data/lib/spruz/xt/partial_application.rb +11 -0
- data/lib/spruz/xt/round.rb +13 -0
- data/lib/spruz/xt/shuffle.rb +11 -0
- data/lib/spruz/xt/subhash.rb +11 -0
- data/lib/spruz/xt/symbol_to_proc.rb +9 -0
- data/lib/spruz/xt/time_dummy.rb +7 -0
- data/lib/spruz/xt/uniq_by.rb +15 -0
- data/tests/test_spruz.rb +431 -0
- metadata +99 -0
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
begin
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
require 'rake/clean'
|
6
|
+
CLEAN.include 'coverage', 'doc'
|
7
|
+
require 'rbconfig'
|
8
|
+
include Config
|
9
|
+
|
10
|
+
PKG_NAME = 'spruz'
|
11
|
+
PKG_VERSION = File.read('VERSION').chomp
|
12
|
+
PKG_FILES = FileList['**/*'].exclude(/(CVS|\.svn|pkg|coverage|doc)/)
|
13
|
+
|
14
|
+
desc "Installing library"
|
15
|
+
task :install do
|
16
|
+
ruby 'install.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Creating documentation"
|
20
|
+
task :doc do
|
21
|
+
ruby 'make_doc.rb'
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Testing library"
|
25
|
+
task :test do
|
26
|
+
ruby '-Ilib tests/test_spruz.rb'
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Testing library with coverage"
|
30
|
+
task :coverage do
|
31
|
+
sh "rcov -Ilib -x 'tests/.*\.rb' tests/test_spruz.rb"
|
32
|
+
end
|
33
|
+
|
34
|
+
if defined? Gem
|
35
|
+
spec = Gem::Specification.new do |s|
|
36
|
+
s.name = PKG_NAME
|
37
|
+
s.version = PKG_VERSION
|
38
|
+
s.summary = "Useful stuff."
|
39
|
+
s.description = "All the stuff that isn't good/big enough for a real library."
|
40
|
+
|
41
|
+
s.files = PKG_FILES
|
42
|
+
|
43
|
+
s.require_path = 'lib'
|
44
|
+
|
45
|
+
s.bindir = "bin"
|
46
|
+
s.executables << "enum"
|
47
|
+
|
48
|
+
s.has_rdoc = true
|
49
|
+
s.extra_rdoc_files << 'README'
|
50
|
+
s.rdoc_options << '--title' << 'Spruz' << '--main' << 'README'
|
51
|
+
s.test_files << 'tests/test_spruz.rb'
|
52
|
+
|
53
|
+
s.author = "Florian Frank"
|
54
|
+
s.email = "flori@ping.de"
|
55
|
+
s.homepage = "http://flori.github.com/spruz"
|
56
|
+
end
|
57
|
+
|
58
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
59
|
+
pkg.need_tar = true
|
60
|
+
pkg.package_files += PKG_FILES
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
desc m = "Writing version information for #{PKG_VERSION}"
|
65
|
+
task :version do
|
66
|
+
puts m
|
67
|
+
File.open(File.join('lib', PKG_NAME, 'version.rb'), 'w') do |v|
|
68
|
+
v.puts <<EOT
|
69
|
+
module Spruz
|
70
|
+
# Spruz version
|
71
|
+
VERSION = '#{PKG_VERSION}'
|
72
|
+
VERSION_ARRAY = VERSION.split(/\\./).map { |x| x.to_i } # :nodoc:
|
73
|
+
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
|
74
|
+
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
|
75
|
+
VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
|
76
|
+
end
|
77
|
+
EOT
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "Default task: write version and test"
|
82
|
+
task :default => [ :version, :test ]
|
83
|
+
|
84
|
+
desc "Prepare a release"
|
85
|
+
task :release => [ :clean, :version, :package ]
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/enum
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'spruz'
|
4
|
+
include Spruz
|
5
|
+
include Spruz::GO
|
6
|
+
|
7
|
+
class CompoundRange
|
8
|
+
def initialize
|
9
|
+
@ranges = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def concat(range)
|
13
|
+
@ranges << range
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(&block)
|
18
|
+
@ranges.each { |r| r.each(&block) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Step
|
23
|
+
def initialize(a, b, step = 1, exclusive = false)
|
24
|
+
@a = a
|
25
|
+
@b = b
|
26
|
+
@step = step
|
27
|
+
if exclusive
|
28
|
+
if step < 0
|
29
|
+
@b += 1
|
30
|
+
else
|
31
|
+
@b -= 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def each(&block)
|
37
|
+
@a.step(@b, @step, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class StringRange
|
42
|
+
def initialize(a, b, exclusive = false)
|
43
|
+
@range = if a > b
|
44
|
+
if exclusive
|
45
|
+
(b.succ..a).to_a.reverse
|
46
|
+
else
|
47
|
+
(b..a).to_a.reverse
|
48
|
+
end
|
49
|
+
else
|
50
|
+
Range.new(a, b, exclusive)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def each(&block)
|
55
|
+
@range.each(&block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class IntegerRange < Step
|
60
|
+
def initialize(a, b, exclusive = false)
|
61
|
+
@a = a
|
62
|
+
@b = b
|
63
|
+
super(a, b, a > b ? -1 : 1, exclusive)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_range(range)
|
68
|
+
case range
|
69
|
+
when /,/
|
70
|
+
range.split(/,/).inject(CompoundRange.new) do |a,x|
|
71
|
+
a.concat(parse_range(x))
|
72
|
+
end
|
73
|
+
when /\A(-?\d+)\Z/
|
74
|
+
($1.to_i)..($1.to_i)
|
75
|
+
when /\A(\w+)\Z/
|
76
|
+
$1..$1
|
77
|
+
when /\A(-?\d+)..(-?\d+)\Z/
|
78
|
+
IntegerRange.new($1.to_i, $2.to_i)
|
79
|
+
when /\A(-?\d+)...(-?\d+)\Z/
|
80
|
+
IntegerRange.new($1.to_i, $2.to_i, true)
|
81
|
+
when /\A(-?\d+)..(-?\d+)([+-]\d+)\Z/
|
82
|
+
Step.new($1.to_i, $2.to_i, $3.to_i)
|
83
|
+
when /\A(-?\d+)...(-?\d+)([+-]\d+)\Z/
|
84
|
+
Step.new($1.to_i, $2.to_i, $3.to_i, true)
|
85
|
+
when /\A(\w+)..(\w+)\Z/
|
86
|
+
StringRange.new($1, $2)
|
87
|
+
when /\A(\w+)...(\w+)\Z/
|
88
|
+
StringRange.new($1, $2, true)
|
89
|
+
else
|
90
|
+
fail "parsing range failed due to '#{range}'"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse_ranges(pattern)
|
95
|
+
pattern.split(/:/).map { |range| parse_range(range) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def execute_command(command, format, tuple)
|
99
|
+
formatted = (format % tuple).split(/:/)
|
100
|
+
if $limited
|
101
|
+
$limited.execute do
|
102
|
+
system sprintf(command, *formatted)
|
103
|
+
end
|
104
|
+
else
|
105
|
+
system sprintf(command, *formatted)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def usage
|
110
|
+
puts <<EOT
|
111
|
+
Usage: #{File.basename($0)} [OPTIONS] RANGES
|
112
|
+
|
113
|
+
RANGES has to be a string of the from R1:R2:...Rn, where R1 to Rn are ranges of
|
114
|
+
values for each dimension. A range can be specified like in one of
|
115
|
+
those examples:
|
116
|
+
2
|
117
|
+
1..3
|
118
|
+
3..1
|
119
|
+
1...3
|
120
|
+
1,2,4
|
121
|
+
1..2,4
|
122
|
+
b
|
123
|
+
a..c
|
124
|
+
a...c
|
125
|
+
c..a
|
126
|
+
c...a
|
127
|
+
a,b,d
|
128
|
+
a..b,d
|
129
|
+
|
130
|
+
|
131
|
+
OPTIONS are
|
132
|
+
|
133
|
+
-p PARALLEL how many threads in parallel should be forked to handle the
|
134
|
+
execution of commands.
|
135
|
+
|
136
|
+
-e COMMAND this command is executed for every created tuple of values.
|
137
|
+
The tuple values can be fetch by using %s in order,
|
138
|
+
e. g. COMMAND = "a=%s;b=%s;echo a is $a. b is $b."
|
139
|
+
|
140
|
+
-f FORMAT format the created values with FORMAT = F1:F2:...:Fn each
|
141
|
+
format is a sprintf percent format, e. g. %02u.
|
142
|
+
|
143
|
+
-h this help display this help
|
144
|
+
|
145
|
+
EOT
|
146
|
+
exit 1
|
147
|
+
end
|
148
|
+
|
149
|
+
$opts = {
|
150
|
+
}.update(go('p:e:f:h')) do |o,default,set|
|
151
|
+
set || default
|
152
|
+
end
|
153
|
+
usage if $opts['h'] || ARGV.size != 1
|
154
|
+
$ranges = ARGV.shift
|
155
|
+
$limited = $opts['p'] ? Limited.new($opts['p']) : nil
|
156
|
+
ranges = parse_ranges($ranges)
|
157
|
+
generator = Generator.new(ranges)
|
158
|
+
$opts['f'] ||= %w[ %s ] * generator.size * ':'
|
159
|
+
|
160
|
+
generator.each do |tuple|
|
161
|
+
if $opts['e']
|
162
|
+
execute_command($opts['e'], $opts['f'], tuple)
|
163
|
+
else
|
164
|
+
puts $opts['f'] % tuple
|
165
|
+
end
|
166
|
+
end
|
167
|
+
exit 0
|
data/install.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rbconfig'
|
4
|
+
include Config
|
5
|
+
require 'fileutils'
|
6
|
+
include FileUtils::Verbose
|
7
|
+
|
8
|
+
if $DEBUG
|
9
|
+
def install(*a)
|
10
|
+
p [ :install ] + a
|
11
|
+
end
|
12
|
+
|
13
|
+
def mkdir_p(*a)
|
14
|
+
p [ :mkdir_p ] + a
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
file = File.join('lib', 'spruz.rb')
|
19
|
+
dst = CONFIG["sitelibdir"]
|
20
|
+
install file, File.join(dst, File.join('lib/spruz.rb'))
|
21
|
+
src = File.join(*%w[lib spruz *.rb])
|
22
|
+
dst = File.join(CONFIG["sitelibdir"], 'spruz')
|
23
|
+
mkdir_p dst
|
24
|
+
for file in Dir[src]
|
25
|
+
install file, File.join(dst, file)
|
26
|
+
end
|
27
|
+
src = File.join(*%w[lib spruz xt *.rb])
|
28
|
+
dst = File.join(CONFIG["sitelibdir"], 'spruz', 'xt')
|
29
|
+
mkdir_p dst
|
30
|
+
for file in Dir[src]
|
31
|
+
install file, File.join(dst, file)
|
32
|
+
end
|
33
|
+
install 'bin/enum', File.join(CONFIG['bindir'], 'enum')
|
data/lib/spruz.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Spruz
|
2
|
+
require 'spruz/version'
|
3
|
+
require 'spruz/bijection'
|
4
|
+
require 'spruz/count_by'
|
5
|
+
require 'spruz/generator'
|
6
|
+
require 'spruz/go'
|
7
|
+
require 'spruz/hash_union'
|
8
|
+
require 'spruz/limited'
|
9
|
+
require 'spruz/memoize'
|
10
|
+
require 'spruz/minimize'
|
11
|
+
require 'spruz/module_group'
|
12
|
+
require 'spruz/null'
|
13
|
+
require 'spruz/once'
|
14
|
+
require 'spruz/p'
|
15
|
+
require 'spruz/partial_application'
|
16
|
+
require 'spruz/round'
|
17
|
+
require 'spruz/shuffle'
|
18
|
+
require 'spruz/subhash'
|
19
|
+
require 'spruz/time_dummy'
|
20
|
+
require 'spruz/to_proc'
|
21
|
+
require 'spruz/uniq_by'
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Spruz
|
2
|
+
class Bijection < Hash
|
3
|
+
def initialize(inverted = Bijection.new(self))
|
4
|
+
@inverted = inverted
|
5
|
+
end
|
6
|
+
|
7
|
+
def fill
|
8
|
+
if empty?
|
9
|
+
yield self
|
10
|
+
freeze
|
11
|
+
end
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def freeze
|
16
|
+
r = super
|
17
|
+
unless @inverted.frozen?
|
18
|
+
@inverted.freeze
|
19
|
+
end
|
20
|
+
r
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key, value)
|
24
|
+
return if key?(key)
|
25
|
+
super
|
26
|
+
@inverted[value] = key
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :inverted
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Spruz
|
2
|
+
# This class can create generator objects, that can produce all tuples, that
|
3
|
+
# would be created by as many for-loops as dimensions were given.
|
4
|
+
#
|
5
|
+
# The generator
|
6
|
+
# g = Spruz::Generator[1..2, %w[a b c]]
|
7
|
+
# produces
|
8
|
+
# g.to_a # => [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"], [2, "c"]]
|
9
|
+
#
|
10
|
+
# The 'each' method can be used to iterate over the tuples
|
11
|
+
# g.each { |a, b| puts "#{a} #{b}" }
|
12
|
+
# and Spruz::Generator includes the Enumerable module, so
|
13
|
+
# Enumerable.instance_methods can be used as well:
|
14
|
+
# g.select { |a, b| %w[a c].include? b } # => [[1, "a"], [1, "c"], [2, "a"], [2, "c"]]
|
15
|
+
#
|
16
|
+
class Generator
|
17
|
+
include Enumerable
|
18
|
+
|
19
|
+
# Create a new Generator object from the enumberables _enums_.
|
20
|
+
def self.[](*enums)
|
21
|
+
new(enums)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create a new Generator instance. Use the objects in the Array _enums_
|
25
|
+
# as dimensions. The should all respond to the :each method (see module
|
26
|
+
# Enumerable in the core ruby library).
|
27
|
+
def initialize(enums)
|
28
|
+
@enums, @iterators, @n = [], [], 0
|
29
|
+
enums.each { |e| add_dimension(e) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Iterate over all tuples produced by this generator and yield to them.
|
33
|
+
def each(&block) # :yield: tuple
|
34
|
+
recurse(&block)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def recurse(tuple = [ nil ] * @n, i = 0, &block)
|
39
|
+
if i < @n - 1 then
|
40
|
+
@enums[i].__send__(@iterators[i]) do |x|
|
41
|
+
tuple[i] = x
|
42
|
+
recurse(tuple, i + 1, &block)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@enums[i].__send__(@iterators[i]) do |x|
|
46
|
+
tuple[i] = x
|
47
|
+
yield tuple.dup
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
private :recurse
|
52
|
+
|
53
|
+
# Add another dimension to this generator. _enum_ is an object, that ought
|
54
|
+
# to respond to the _iterator_ method (defaults to :each).
|
55
|
+
def add_dimension(enum, iterator = :each)
|
56
|
+
@enums << enum
|
57
|
+
@iterators << iterator
|
58
|
+
@n += 1
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return the size of this generator, that is the number of its dimensions.
|
62
|
+
def size
|
63
|
+
@enums.size
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/spruz/go.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Spruz
|
2
|
+
module GO
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# Parses the argument array _args_, according to the pattern _s_, to
|
6
|
+
# retrieve the single character command line options from it. If _s_ is
|
7
|
+
# 'xy:' an option '-x' without an option argument is searched, and an
|
8
|
+
# option '-y foo' with an option argument ('foo').
|
9
|
+
#
|
10
|
+
# An option hash is returned with all found options set to true or the
|
11
|
+
# found option argument.
|
12
|
+
def go(s, args = ARGV)
|
13
|
+
b,v = s.scan(/(.)(:?)/).inject([{},{}]) { |t,(o,a)|
|
14
|
+
a = a == ':'
|
15
|
+
t[a ? 1 : 0][o] = a ? nil : false
|
16
|
+
t
|
17
|
+
}
|
18
|
+
while a = args.shift
|
19
|
+
a !~ /\A-(.+)/ and args.unshift a and break
|
20
|
+
p = $1
|
21
|
+
until p == ''
|
22
|
+
o = p.slice!(0, 1)
|
23
|
+
if v.key?(o)
|
24
|
+
v[o] = if p == '' then args.shift or break 1 else p end
|
25
|
+
break
|
26
|
+
elsif b.key?(o)
|
27
|
+
b[o] = true
|
28
|
+
else
|
29
|
+
args.unshift a
|
30
|
+
break 1
|
31
|
+
end
|
32
|
+
end and break
|
33
|
+
end
|
34
|
+
b.merge(v)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|