numru-misc 0.1.1
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/ChangeLog +41 -0
- data/LICENSE.txt +34 -0
- data/Rakefile +38 -0
- data/doc/emath.html +26 -0
- data/doc/index.html +83 -0
- data/doc/keywordopt.html +179 -0
- data/doc/md_iterators.html +86 -0
- data/doc/misc.html +83 -0
- data/install.rb +104 -0
- data/lib/numru/misc.rb +4 -0
- data/lib/numru/misc/emath.rb +75 -0
- data/lib/numru/misc/keywordopt.rb +498 -0
- data/lib/numru/misc/md_iterators.rb +180 -0
- data/lib/numru/misc/misc.rb +196 -0
- data/makedoc.csh +6 -0
- metadata +97 -0
data/doc/misc.html
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
<?xml version="1.0" ?>
|
2
|
+
<!DOCTYPE html
|
3
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
4
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
5
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
6
|
+
<head>
|
7
|
+
<title>lib/numru/misc/misc.rb</title>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<h1><a name="label:0" id="label:0">module NumRu::Misc</a></h1><!-- RDLabel: "module NumRu::Misc" -->
|
11
|
+
<h2><a name="label:1" id="label:1">Overview</a></h2><!-- RDLabel: "Overview" -->
|
12
|
+
<p>Miscellaneous functions and classes to facilitate programming.</p>
|
13
|
+
<h2><a name="label:2" id="label:2">Index</a></h2><!-- RDLabel: "Index" -->
|
14
|
+
<p>CLASSES</p>
|
15
|
+
<ul>
|
16
|
+
<li><a href="keywordopt.html">class KeywordOpt</a>
|
17
|
+
to support keyward arguments with default values.</li>
|
18
|
+
<li><a href="narray_ext.html">class NArray (enhancement of NArray made by M Tanaka)</a></li>
|
19
|
+
</ul>
|
20
|
+
<p>MODULES</p>
|
21
|
+
<ul>
|
22
|
+
<li><a href="md_iterators.html">module MD_Iterators</a> A Mixin for classes with
|
23
|
+
multi-dimension indexing support (such as NArray).</li>
|
24
|
+
<li><a href="emath.html">module EMath</a>
|
25
|
+
To be included instead of the Math predefined module (or NMath in NArray).
|
26
|
+
Unlike Math and NMath, EMath handles unknown classes by calling its
|
27
|
+
native instance method (assuming the same name).</li>
|
28
|
+
</ul>
|
29
|
+
<p>MODULE FUNCTIONS</p>
|
30
|
+
<ul>
|
31
|
+
<li><a href="#label:4">check_shape_consistency</a></li>
|
32
|
+
</ul>
|
33
|
+
<h2><a name="label:3" id="label:3">Module functions</a></h2><!-- RDLabel: "Module functions" -->
|
34
|
+
<dl>
|
35
|
+
<dt><a name="label:4" id="label:4"><code>check_shape_consistency(<var>cshapes</var>, *<var>args</var>)</code></a></dt><!-- RDLabel: "check_shape_consistency" -->
|
36
|
+
<dd>
|
37
|
+
<p>Check the consistency of array shapes (multi-dim such as NArray).
|
38
|
+
Exception is raised if inconsistent.</p>
|
39
|
+
<p>ARGUMENTS</p>
|
40
|
+
<ul>
|
41
|
+
<li>cshapes (String) : description of the shapes of the args.
|
42
|
+
Delimited by one-or-more spaces between arrays,
|
43
|
+
and the shape of each array is delimited by a comma. The lengths are
|
44
|
+
expressed with string names as identifiers (in that case, length
|
45
|
+
values are unquestioned) or specified as positive integers.
|
46
|
+
Use '..' or '...' for repetition of the last shape.
|
47
|
+
See EXAMPLES below.</li>
|
48
|
+
<li>args (multi-dim arrays such as NArray): arrays to be checked</li>
|
49
|
+
</ul>
|
50
|
+
<p>RETURN VALUE</p>
|
51
|
+
<ul>
|
52
|
+
<li>nil</li>
|
53
|
+
</ul>
|
54
|
+
<p>POSSIBLE EXCEPTIONS</p>
|
55
|
+
<ul>
|
56
|
+
<li>exception is raised if cshapes and args are inconsistent:
|
57
|
+
<ul>
|
58
|
+
<li>RuntimeError, if the arrays do not have shapes specified by cshapes.</li>
|
59
|
+
<li>ArgeumentError, if the number of args are inconsistent with cshapes.
|
60
|
+
This is likely a coding error of the user.</li>
|
61
|
+
</ul></li>
|
62
|
+
</ul>
|
63
|
+
<p>EXAMPLES</p>
|
64
|
+
<ul>
|
65
|
+
<li><p>to check whether three arrays u, v, and w are shaped as
|
66
|
+
u[nx], v[ny], and w[nx,ny], where nx and ny are any integer:</p>
|
67
|
+
<pre>NumRu::Misc.check_shape_consistency('nx ny nx,ny',u,v,w)</pre>
|
68
|
+
<p>Or equivalently,</p>
|
69
|
+
<pre>NumRu::Misc.check_shape_consistency('m n m,n',u,v,w)</pre>
|
70
|
+
<p>because actual strings does not matter.</p></li>
|
71
|
+
<li><p>To specify fixed lengths, use integers instead of names:</p>
|
72
|
+
<pre>NumRu::Misc.check_shape_consistency('4 n 4,n',u,v,w)</pre>
|
73
|
+
<p>In this case, u,v,w must have shapes [4], [ny], and [4,ny],
|
74
|
+
where ny is any length.</p></li>
|
75
|
+
<li><p>Use '..' or '...' to repeat the same shape:</p>
|
76
|
+
<pre>NumRu::Misc.check_shape_consistency('nx,ny ...',u,v,w)</pre>
|
77
|
+
<p>This ensures that u, v, and w are 2D arrays with the same shape.
|
78
|
+
Note: '..' and '...' are the same, so you can use whichever you like.</p></li>
|
79
|
+
</ul></dd>
|
80
|
+
</dl>
|
81
|
+
|
82
|
+
</body>
|
83
|
+
</html>
|
data/install.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'find'
|
3
|
+
include Config
|
4
|
+
|
5
|
+
if CONFIG["MINOR"].to_i > 6 then $rb_18 = true else $rb_18 = false end
|
6
|
+
if $rb_18
|
7
|
+
require 'fileutils'
|
8
|
+
else
|
9
|
+
require 'ftools'
|
10
|
+
end
|
11
|
+
|
12
|
+
=begin
|
13
|
+
$version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
14
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", $version)
|
15
|
+
# $archdir = File.join($libdir, CONFIG["arch"])
|
16
|
+
$site_libdir = $:.find {|x| x =~ /site_ruby$/}
|
17
|
+
if !$site_libdir
|
18
|
+
$site_libdir = File.join($libdir, "site_ruby")
|
19
|
+
elsif Regexp.compile($site_libdir) !~ Regexp.quote($version)
|
20
|
+
$site_libdir = File.join($site_libdir, $version)
|
21
|
+
end
|
22
|
+
|
23
|
+
default_destdir = $site_libdir
|
24
|
+
=end
|
25
|
+
|
26
|
+
default_destdir = CONFIG["sitelibdir"]
|
27
|
+
|
28
|
+
def install_rb(srcdir, destdir)
|
29
|
+
libdir = "lib"
|
30
|
+
libdir = File.join(srcdir, libdir) if srcdir
|
31
|
+
path = []
|
32
|
+
dir = []
|
33
|
+
Find.find(libdir) do |f|
|
34
|
+
next unless FileTest.file?(f)
|
35
|
+
next if (f = f[libdir.length+1..-1]) == nil
|
36
|
+
next if (/CVS$/ =~ File.dirname(f))
|
37
|
+
path.push f
|
38
|
+
dir |= [File.dirname(f)]
|
39
|
+
end
|
40
|
+
for f in dir
|
41
|
+
next if f == "."
|
42
|
+
next if f == "CVS"
|
43
|
+
if $rb_18
|
44
|
+
FileUtils.makedirs(File.join(destdir, f))
|
45
|
+
else
|
46
|
+
File::makedirs(File.join(destdir, f))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
for f in path
|
50
|
+
next if (/\~$/ =~ f)
|
51
|
+
next if (/^\./ =~ File.basename(f))
|
52
|
+
if $rb_18
|
53
|
+
FileUtils.install(File.join("lib", f), File.join(destdir, f), {:mode => 0644, :verbose => true})
|
54
|
+
else
|
55
|
+
File::install(File.join("lib", f), File.join(destdir, f), 0644, true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def ARGV.switch
|
61
|
+
return nil if self.empty?
|
62
|
+
arg = self.shift
|
63
|
+
return nil if arg == '--'
|
64
|
+
if arg =~ /^-(.)(.*)/
|
65
|
+
return arg if $1 == '-'
|
66
|
+
raise 'unknown switch "-"' if $2.index('-')
|
67
|
+
self.unshift "-#{$2}" if $2.size > 0
|
68
|
+
"-#{$1}"
|
69
|
+
else
|
70
|
+
self.unshift arg
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def ARGV.req_arg
|
76
|
+
self.shift || raise('missing argument')
|
77
|
+
end
|
78
|
+
|
79
|
+
destdir = default_destdir
|
80
|
+
|
81
|
+
begin
|
82
|
+
while switch = ARGV.switch
|
83
|
+
case switch
|
84
|
+
when '-d', '--destdir'
|
85
|
+
destdir = ARGV.req_arg
|
86
|
+
# when '-u', '--uninstall'
|
87
|
+
# uninstall = true
|
88
|
+
else
|
89
|
+
raise "unknown switch #{switch.dump}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
rescue
|
93
|
+
STDERR.puts $!.to_s
|
94
|
+
STDERR.puts File.basename($0) +
|
95
|
+
" -d <destdir>"
|
96
|
+
exit 1
|
97
|
+
end
|
98
|
+
|
99
|
+
#if( defined?(uninstall) && uninstall )
|
100
|
+
# uninstall_rb(nil, destdir)
|
101
|
+
#else
|
102
|
+
install_rb(nil, destdir)
|
103
|
+
#end
|
104
|
+
|
data/lib/numru/misc.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
=begin
|
2
|
+
=module NumRu::Misc::EMath
|
3
|
+
|
4
|
+
To be included instead of the Math predefined module (or NMath in NArray).
|
5
|
+
Unlike Math and NMath, EMath handles unknown classes by calling its
|
6
|
+
native instance method (assuming the same name).
|
7
|
+
|
8
|
+
Therefore, if included, its function (module method) is used as:
|
9
|
+
|
10
|
+
cos( obj )
|
11
|
+
|
12
|
+
and so on. If obj is not of a supported class, EMath calls, obj.cos in
|
13
|
+
this case. (If cos is not a method of obj, then an exception is
|
14
|
+
raised.) Supported classes are Numeric (by Math) and NArray (by
|
15
|
+
NMath). EMath stands for "good Math" (for obvious reason for a
|
16
|
+
Japanese).
|
17
|
+
|
18
|
+
Note: as for atan2(a,b), a.atan2(b) will be called if a or b
|
19
|
+
is not supported. This is the case for all functions that take
|
20
|
+
two or more arguments.
|
21
|
+
|
22
|
+
=end
|
23
|
+
|
24
|
+
require "narray"
|
25
|
+
|
26
|
+
module NumRu
|
27
|
+
module Misc
|
28
|
+
module EMath
|
29
|
+
|
30
|
+
E = Math::E
|
31
|
+
PI = Math::PI
|
32
|
+
|
33
|
+
funcs = ["acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh",
|
34
|
+
"cos", "cosh", "erf", "erfc", "exp", "frexp", "hypot",
|
35
|
+
"ldexp", "log", "log10", "sin", "sinh", "sqrt", "tan", "tanh"]
|
36
|
+
# FUNCS: from ruby 1.8.0, ( Math.methods - Object.methods ).sort
|
37
|
+
|
38
|
+
module_function
|
39
|
+
funcs.each{|func|
|
40
|
+
eval <<-EOS,nil,__FILE__,__LINE__+1
|
41
|
+
def #{func}(*arg)
|
42
|
+
case arg[0]
|
43
|
+
when Numeric
|
44
|
+
Math.#{func}(*arg)
|
45
|
+
when NArray
|
46
|
+
NMath.#{func}(*arg)
|
47
|
+
else
|
48
|
+
obj = arg.shift
|
49
|
+
begin
|
50
|
+
obj.#{func}(*arg)
|
51
|
+
rescue NameError
|
52
|
+
raise TypeError,"cannot handle \#{obj.class}: \#{obj.inspect}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
EOS
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if __FILE__ == $0
|
63
|
+
include NumRu::Misc::EMath
|
64
|
+
p cos( PI )
|
65
|
+
p cos( Complex(0, 1) )
|
66
|
+
p cos( NArray[0,PI/3,PI/2] )
|
67
|
+
begin
|
68
|
+
p cos( "ggg" )
|
69
|
+
rescue
|
70
|
+
print "* error as expected >>: ",$!,"\n"
|
71
|
+
end
|
72
|
+
require 'narray_miss'
|
73
|
+
nam = NArrayMiss.to_nam( NArray[0,PI/3,PI/2] )
|
74
|
+
p cos(nam)
|
75
|
+
end
|
@@ -0,0 +1,498 @@
|
|
1
|
+
=begin
|
2
|
+
==Index
|
3
|
+
* ((<class NumRu::Misc::KeywordOpt>))
|
4
|
+
* ((<class NumRu::Misc::KeywordOptAutoHelp < NumRu::Misc::KeywordOpt>))
|
5
|
+
|
6
|
+
= class NumRu::Misc::KeywordOpt
|
7
|
+
|
8
|
+
== Overview
|
9
|
+
|
10
|
+
A class to facilitate optional keyword arguments. More specifically,
|
11
|
+
it helps the use of a Hash to mimic the keyword argument system.
|
12
|
+
With this, you can set default values and description to each
|
13
|
+
keyword argument.
|
14
|
+
|
15
|
+
== Classes defined supplementarilly
|
16
|
+
|
17
|
+
=== class NumRu::Misc::HelpMessagingException < StandardError
|
18
|
+
|
19
|
+
This is for your convenience. See the usage example below.
|
20
|
+
|
21
|
+
== Usage example
|
22
|
+
|
23
|
+
Suppose that you introduce keyword arguments "flag" and "number"
|
24
|
+
to the method "hoge" in a class/module Foo. It can be done as
|
25
|
+
follows:
|
26
|
+
|
27
|
+
require 'numru/misc' # or, specifically, require 'numru/misc/keywordopt'
|
28
|
+
include NumRu
|
29
|
+
|
30
|
+
class Foo
|
31
|
+
@@opt_hoge = Misc::KeywordOpt.new(
|
32
|
+
['flag', false, 'whether or not ...'],
|
33
|
+
['number', 1, 'number of ...'],
|
34
|
+
['help', false, 'show help message']
|
35
|
+
)
|
36
|
+
def hoge(regular_arg1, regular_arg2, options=nil)
|
37
|
+
opt = @@opt_hoge.interpret(options)
|
38
|
+
if opt['help']
|
39
|
+
puts @@opt_hoge.help
|
40
|
+
puts ' Current values='+opt.inspect
|
41
|
+
raise Misc::HelpMessagingException, '** show help message and raise **'
|
42
|
+
end
|
43
|
+
# do what you want below
|
44
|
+
# (options are set in the Hash opt: opt['flag'] and opt['number'])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Here, the options are defined in the class variable @@opt_hoge
|
49
|
+
with option names, default values, and descriptions (for help
|
50
|
+
messaging). One can use the method hoge as follows:
|
51
|
+
|
52
|
+
foo = Foo.new
|
53
|
+
...
|
54
|
+
x = ...
|
55
|
+
y = ...
|
56
|
+
...
|
57
|
+
foo.hoge( x, y, {'flag'=>true, 'number'=>10} )
|
58
|
+
|
59
|
+
Or equivalently,
|
60
|
+
|
61
|
+
foo.hoge( x, y, 'flag'=>true, 'number'=>10 )
|
62
|
+
|
63
|
+
because '{}' can be omitted here.
|
64
|
+
|
65
|
+
Tails of options names can be shortened as long as unambiguous:
|
66
|
+
|
67
|
+
foo.hoge( x, y, 'fla'=>true, 'num'=>10 )
|
68
|
+
|
69
|
+
|
70
|
+
To show the help message, call
|
71
|
+
|
72
|
+
foo.hoge( x, y, 'help'=>true )
|
73
|
+
|
74
|
+
This will cause the following help message printed with the
|
75
|
+
exception HelpMessagingException raised.
|
76
|
+
|
77
|
+
<< Description of options >>
|
78
|
+
option name => default value description:
|
79
|
+
"flag" => false whether or not ...
|
80
|
+
"number" => 1 number of ...
|
81
|
+
"help" => false show help message
|
82
|
+
Current values={"help"=>true, "number"=>1, "flag"=>false}
|
83
|
+
NumRu::Misc::HelpMessagingException: ** help messaging done **
|
84
|
+
from (irb):78:in "hoge"
|
85
|
+
from (irb):83
|
86
|
+
|
87
|
+
Do not affraid to write long descriptions. The help method
|
88
|
+
breaks lines nicely if they are long.
|
89
|
+
|
90
|
+
== Class methods
|
91
|
+
|
92
|
+
---KeywordOpt.new( *args )
|
93
|
+
|
94
|
+
Constructor.
|
95
|
+
|
96
|
+
ARGUMENTS
|
97
|
+
* args : (case 1) arrays of two or three elements: [option name,
|
98
|
+
default value, description ], or [option name, default value]
|
99
|
+
if you do not want to write descriptions. Option names and
|
100
|
+
descriptions must be String. (case 2) another KeywordOpt.
|
101
|
+
Cases 1 and 2 can be mixed.
|
102
|
+
|
103
|
+
When case 2, a link to the other KeywordOpt is kept. Thus, change
|
104
|
+
of values in it is reflected to the current one. However,
|
105
|
+
the link is deleted if values are changed by ((<set>)).
|
106
|
+
|
107
|
+
RETURN VALUE
|
108
|
+
* a KeywordOpt object
|
109
|
+
|
110
|
+
EXAMPLE
|
111
|
+
* case 1
|
112
|
+
opt = Misc::KeywordOpt.new(
|
113
|
+
['flag', false, 'whether or not ...'],
|
114
|
+
['help', false, 'show help message']
|
115
|
+
)
|
116
|
+
* case 2
|
117
|
+
opt = Misc::KeywordOpt.new( optA, optB )
|
118
|
+
* case 1 & 2
|
119
|
+
opt = Misc::KeywordOpt.new(
|
120
|
+
['flag', false, 'whether or not ...'],
|
121
|
+
optA
|
122
|
+
)
|
123
|
+
|
124
|
+
== Methods
|
125
|
+
---interpret(hash)
|
126
|
+
|
127
|
+
Interprets a hash that specifies option values.
|
128
|
+
|
129
|
+
ARGUMENTS
|
130
|
+
* hash (Hash or nil) : a hash with string keys matching option names
|
131
|
+
(initializedwhen constructed). The matching is case sensitive and done
|
132
|
+
such that the tail of a option name can be omitted as long as
|
133
|
+
unambiguous (for example, 'num' for 'number').
|
134
|
+
If the argument is nil, the current values are returned.
|
135
|
+
If there are two options like 'max' and 'maxval', to use
|
136
|
+
a key 'max' (identical to the former paramer) is allowed, although
|
137
|
+
it matches 'maxval' as well. (Again 'ma' is regarded ambiguous.)
|
138
|
+
|
139
|
+
RETURN VALUE
|
140
|
+
* a Hash containing the option values (default values overwritten
|
141
|
+
with hash).
|
142
|
+
|
143
|
+
POSSIBLE EXCEPTION
|
144
|
+
* hash has a key that does not match any of the option names.
|
145
|
+
* hash has a key that is ambiguous
|
146
|
+
|
147
|
+
---set(hash)
|
148
|
+
|
149
|
+
Similar to ((<interpret>)) but changes internal values.
|
150
|
+
|
151
|
+
ARGUMENTS
|
152
|
+
* hash (Hash) : see ((<interpret>)). (Here, nil is not permitted though)
|
153
|
+
|
154
|
+
RETURN VALUE
|
155
|
+
* a Hash containing the values replaced (the ones before calling this
|
156
|
+
method)
|
157
|
+
|
158
|
+
POSSIBLE EXCEPTION
|
159
|
+
* the argument is not a Hash
|
160
|
+
* others are same as in ((<interpret>))
|
161
|
+
|
162
|
+
---help
|
163
|
+
|
164
|
+
Returns a help message
|
165
|
+
|
166
|
+
RETURN VALUE
|
167
|
+
* a String describing the option names, default values, and descriptions
|
168
|
+
|
169
|
+
---[](key)
|
170
|
+
|
171
|
+
Returns a value associated with the key (exact matching unlike interpret)
|
172
|
+
|
173
|
+
---keys
|
174
|
+
|
175
|
+
Retunrs the keys.
|
176
|
+
|
177
|
+
---select_existent(hash_or_keys)
|
178
|
+
|
179
|
+
Copies hash_or_keys, exclude ones that are not included in the option
|
180
|
+
(by comparing keys), and returns it. I.e. select only the ones
|
181
|
+
exsitent.
|
182
|
+
|
183
|
+
NOTE: ambiguity is not checked, so the resultant value is not
|
184
|
+
necessarily accepted by ((<interpret>)).
|
185
|
+
|
186
|
+
ARGUMENTS
|
187
|
+
* hash_or_keys (Hash or Array)
|
188
|
+
|
189
|
+
RETURN VALUE
|
190
|
+
* a Hash or Array depending on the class of the argument hash_or_keys
|
191
|
+
|
192
|
+
= class NumRu::Misc::KeywordOptAutoHelp < NumRu::Misc::KeywordOpt
|
193
|
+
|
194
|
+
Same as ((<class NumRu::Misc::KeywordOpt>)), but the method ((<interpret>))
|
195
|
+
shows a help message and raise an exception if option 'help' is provided
|
196
|
+
as an argument and is not nil or false
|
197
|
+
((({NumRu::Misc::HelpMessagingException < StandardError})))
|
198
|
+
or if the arguments cannot be interpreted correctly ((({ArgumentError}))).
|
199
|
+
Option 'help' is automatically defined, so you do not have to define it
|
200
|
+
yourself.
|
201
|
+
|
202
|
+
=end
|
203
|
+
|
204
|
+
module NumRu
|
205
|
+
|
206
|
+
module Misc
|
207
|
+
class HelpMessagingException < StandardError
|
208
|
+
end
|
209
|
+
|
210
|
+
class KeywordOpt
|
211
|
+
def initialize(*args)
|
212
|
+
# USAGE:
|
213
|
+
# KeywordOpt.new([key,val,description],[key,val,description],..)
|
214
|
+
# where key is a String, and description can be omitted.
|
215
|
+
@val=Hash.new
|
216
|
+
@description=Hash.new
|
217
|
+
@keys = []
|
218
|
+
args.each{ |x|
|
219
|
+
case x
|
220
|
+
when Array
|
221
|
+
unless (x[0]=='help') && @keys.include?(x[0])
|
222
|
+
#^only 'help' can overwrap in the arguments
|
223
|
+
@keys.push(x[0])
|
224
|
+
@val[x[0]] = x[1]
|
225
|
+
@description[x[0]] = ( (x.length>=3) ? x[2] : '' )
|
226
|
+
end
|
227
|
+
when KeywordOpt
|
228
|
+
x.keys.each{|k|
|
229
|
+
unless k=='help' && @keys.include?(k)
|
230
|
+
#^only 'help' can overwrap in the arguments
|
231
|
+
@keys.push(k)
|
232
|
+
@val[k] = x #.val[k]
|
233
|
+
@description[k] = x.description[k]
|
234
|
+
end
|
235
|
+
}
|
236
|
+
def @val.[](k)
|
237
|
+
val = super(k)
|
238
|
+
val.is_a?(KeywordOpt) ? val[k] : val
|
239
|
+
end
|
240
|
+
def @val.dup
|
241
|
+
out = Hash.new
|
242
|
+
each{|k,val| out[k] = (val.is_a?(KeywordOpt) ? val[k] : val)}
|
243
|
+
out
|
244
|
+
end
|
245
|
+
else
|
246
|
+
raise ArgumentError, "invalid argument: #{x.inspect}"
|
247
|
+
end
|
248
|
+
}
|
249
|
+
@keys_sort = @keys.sort
|
250
|
+
if @keys_sort.length != @keys_sort.uniq.length
|
251
|
+
raise ArgumentError, "keys are not unique"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def interpret(hash)
|
256
|
+
return @val.dup if hash.nil?
|
257
|
+
##
|
258
|
+
len = @val.length
|
259
|
+
im = 0
|
260
|
+
out = @val.dup
|
261
|
+
hash.keys.sort.each do |key|
|
262
|
+
rkey = /^#{key}/
|
263
|
+
loop do
|
264
|
+
if rkey =~ @keys_sort[im]
|
265
|
+
if im<len-1 && rkey=~@keys_sort[im+1] &&
|
266
|
+
key != @keys_sort[im] # not identical
|
267
|
+
raise ArgumentError, "Ambiguous key specification '#{key}'."
|
268
|
+
end
|
269
|
+
out[@keys_sort[im]]=hash[key]
|
270
|
+
break
|
271
|
+
end
|
272
|
+
im += 1
|
273
|
+
if im==len
|
274
|
+
raise ArgumentError, "'#{key}' does not match any of the keys."
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
out
|
279
|
+
end
|
280
|
+
|
281
|
+
def select_existent(hash_or_keys)
|
282
|
+
hash_or_keys = hash_or_keys.dup # not to alter the original
|
283
|
+
len = @val.length
|
284
|
+
im = 0
|
285
|
+
kys = ( Array === hash_or_keys ? hash_or_keys : hash_or_keys.keys )
|
286
|
+
kys.sort.each do |key|
|
287
|
+
rkey = /^#{key}/
|
288
|
+
loop do
|
289
|
+
break if rkey =~ @keys_sort[im]
|
290
|
+
im += 1
|
291
|
+
if im==len
|
292
|
+
hash_or_keys.delete(key)
|
293
|
+
im = 0 # rewind
|
294
|
+
break
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
hash_or_keys
|
299
|
+
end
|
300
|
+
|
301
|
+
def set(hash)
|
302
|
+
raise ArgumentError, "not a hash" if !hash.is_a?(Hash)
|
303
|
+
##
|
304
|
+
replaced = Hash.new
|
305
|
+
len = @val.length
|
306
|
+
im = 0
|
307
|
+
hash.keys.sort.each do |key|
|
308
|
+
rkey = /^#{key}/
|
309
|
+
loop do
|
310
|
+
if rkey =~ @keys_sort[im]
|
311
|
+
if im<len-1 && rkey=~@keys_sort[im+1]
|
312
|
+
raise "Ambiguous key specification '#{key}'."
|
313
|
+
end
|
314
|
+
replaced[@keys_sort[im]] = @val[@keys_sort[im]]
|
315
|
+
@val[@keys_sort[im]]=hash[key]
|
316
|
+
break
|
317
|
+
end
|
318
|
+
im += 1
|
319
|
+
raise "'#{key}' does not match any of the keys." if im==len
|
320
|
+
end
|
321
|
+
end
|
322
|
+
replaced
|
323
|
+
end
|
324
|
+
|
325
|
+
# def __line_feed(str)
|
326
|
+
# if str.length >= 68
|
327
|
+
# idx = str[0..67].rindex(/\s/)
|
328
|
+
# if idx
|
329
|
+
# str[idx, 1] = "\n\t"
|
330
|
+
# end
|
331
|
+
# end
|
332
|
+
# str
|
333
|
+
# end
|
334
|
+
def __line_feed(str, len)
|
335
|
+
if str.length >= len
|
336
|
+
idx = str[0...len].rindex(/\s/)
|
337
|
+
if idx
|
338
|
+
str = str[0...idx] + "\n\t\t\t# " + __line_feed(str[(idx+1)..-1],50)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
str
|
342
|
+
end
|
343
|
+
private :__line_feed
|
344
|
+
|
345
|
+
def help
|
346
|
+
" option name\tdefault value\t# description:\n" +
|
347
|
+
@keys.collect{|k|
|
348
|
+
__line_feed(" #{k.inspect}\t#{@val[k].inspect}\t# #{@description[k]}", 66)
|
349
|
+
}.join("\n")
|
350
|
+
end
|
351
|
+
|
352
|
+
def [](k)
|
353
|
+
v = @val[k]
|
354
|
+
if v.is_a?(KeywordOpt)
|
355
|
+
v = v.val[k]
|
356
|
+
end
|
357
|
+
v
|
358
|
+
end
|
359
|
+
|
360
|
+
def keys
|
361
|
+
@keys.dup
|
362
|
+
end
|
363
|
+
|
364
|
+
##### protected methods #####
|
365
|
+
protected
|
366
|
+
attr_reader :val, :description
|
367
|
+
end
|
368
|
+
|
369
|
+
##################################################
|
370
|
+
|
371
|
+
class KeywordOptAutoHelp < KeywordOpt
|
372
|
+
def initialize(*args)
|
373
|
+
args.push(['help', false, 'show help message if true'])
|
374
|
+
super(*args)
|
375
|
+
end
|
376
|
+
|
377
|
+
def interpret(hash)
|
378
|
+
begin
|
379
|
+
out = super(hash)
|
380
|
+
rescue
|
381
|
+
raise $!.inspect + "\n Available parameters are:\n" + help
|
382
|
+
end
|
383
|
+
if out['help']
|
384
|
+
puts "<< Description of options >>\n" + help
|
385
|
+
puts ' Current values=' + out.inspect
|
386
|
+
raise Misc::HelpMessagingException, '** help messaging done **'
|
387
|
+
end
|
388
|
+
out
|
389
|
+
end
|
390
|
+
|
391
|
+
def set(hash)
|
392
|
+
raise ArgumentError, "not a hash" if !hash.is_a?(Hash)
|
393
|
+
if hash['help']
|
394
|
+
puts "<< Description of options >>\n" + help
|
395
|
+
raise Misc::HelpMessagingException, '** help messaging done **'
|
396
|
+
end
|
397
|
+
super
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
if __FILE__ == $0
|
405
|
+
include NumRu
|
406
|
+
|
407
|
+
class Foo
|
408
|
+
@@opt_hoge = Misc::KeywordOpt.new(
|
409
|
+
['flag', false, 'whether or not ...'],
|
410
|
+
['number', 1, 'number of ...'],
|
411
|
+
['fff', 1, 'fff...'],
|
412
|
+
['help', false, 'show help message']
|
413
|
+
)
|
414
|
+
def self.change_default(hash)
|
415
|
+
@@opt_hoge.set(hash)
|
416
|
+
end
|
417
|
+
def hoge(regular_arg1, regular_arg2, options=nil)
|
418
|
+
opt = @@opt_hoge.interpret(options)
|
419
|
+
if opt['help']
|
420
|
+
puts "* Description of options:\n" + @@opt_hoge.help
|
421
|
+
puts ' Current values='+opt.inspect
|
422
|
+
raise Misc::HelpMessagingException, '** show help message and raise **'
|
423
|
+
end
|
424
|
+
# do what you want below
|
425
|
+
# (options are set in the Hash opt: opt['flag'] and opt['number'])
|
426
|
+
p opt
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
foo = Foo.new
|
431
|
+
x = 1
|
432
|
+
y = 1
|
433
|
+
print "### 0 ###\n"
|
434
|
+
foo.hoge( x, y, {'flag'=>true, 'number'=>10} )
|
435
|
+
foo.hoge( x, y )
|
436
|
+
print "### 1 ###\n"
|
437
|
+
foo.hoge( x, y, 'fla'=>true, 'num'=>10 )
|
438
|
+
print "### 2 ###\n"
|
439
|
+
begin
|
440
|
+
foo.hoge( x, y, 'help'=>true )
|
441
|
+
rescue
|
442
|
+
puts $!
|
443
|
+
end
|
444
|
+
print "### 3 ###\n"
|
445
|
+
Foo.change_default( {'number'=>3} )
|
446
|
+
begin
|
447
|
+
foo.hoge( x, y, 'fla'=>true, 'num'=>10, 'help'=>true)
|
448
|
+
rescue
|
449
|
+
puts $!
|
450
|
+
end
|
451
|
+
print "### 4 ###\n"
|
452
|
+
begin
|
453
|
+
foo.hoge( x, y, 'dummy'=>nil)
|
454
|
+
rescue
|
455
|
+
puts $!
|
456
|
+
end
|
457
|
+
print "### 5 ###\n"
|
458
|
+
begin
|
459
|
+
foo.hoge( x, y, 'f'=>nil)
|
460
|
+
rescue
|
461
|
+
puts $!
|
462
|
+
end
|
463
|
+
|
464
|
+
print "\n###### test of KeywordOptAutoHelp ######\n"
|
465
|
+
opt = Misc::KeywordOptAutoHelp.new(
|
466
|
+
['flag', false, 'whether or not ...'],
|
467
|
+
['number', 1, 'number of ...']
|
468
|
+
)
|
469
|
+
print "### 11 ###\n"
|
470
|
+
begin
|
471
|
+
opt.interpret('flag'=>10,'help'=>true)
|
472
|
+
rescue
|
473
|
+
puts $!
|
474
|
+
end
|
475
|
+
print "### 12 ###\n"
|
476
|
+
begin
|
477
|
+
opt.interpret('nnn'=>10)
|
478
|
+
rescue
|
479
|
+
puts $!
|
480
|
+
end
|
481
|
+
|
482
|
+
print "### 13 ###\n"
|
483
|
+
opt2 = Misc::KeywordOptAutoHelp.new(
|
484
|
+
['flafla', false, 'whether or not ...']
|
485
|
+
)
|
486
|
+
opt3 = Misc::KeywordOptAutoHelp.new( opt, opt2 )
|
487
|
+
p opt3.interpret('flag'=>true)
|
488
|
+
begin
|
489
|
+
opt3.interpret('help'=>true)
|
490
|
+
rescue
|
491
|
+
puts $!
|
492
|
+
end
|
493
|
+
|
494
|
+
print "### 14 ###\n"
|
495
|
+
p opt2.keys, opt.keys
|
496
|
+
p opt.select_existent({"flag"=>99, "num"=>88, 'acb'=>333})
|
497
|
+
p opt.select_existent(["flag", "num", 'acb'])
|
498
|
+
end
|