diff --git a/Rakefile b/Rakefile index a90a6d8..d52729b 100644 --- a/Rakefile +++ b/Rakefile @@ -1,15 +1,67 @@ -require 'rubygems' +# require 'rubygems' +# require 'rake' +# require 'echoe' +# require 'lib/version' +# +# Echoe.new('sirb', Sirb::VERSION) do |p| +# p.description = "Generate a unique token with Active Record." +# p.url = "http://github.com/davidrichards/sirb" +# p.author = "David Richards" +# p.email = "drichards@showcase60.com" +# p.ignore_pattern = ["tmp/*", "script/*"] +# p.development_dependencies = [] +# end +# +# Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext } +# require 'rake' -require 'echoe' -require 'lib/version' -Echoe.new('sirb', Sirb::VERSION) do |p| - p.description = "Generate a unique token with Active Record." - p.url = "http://github.com/davidrichards/sirb" - p.author = "David Richards" - p.email = "drichards@showcase60.com" - p.ignore_pattern = ["tmp/*", "script/*"] - p.development_dependencies = [] +begin + require 'jeweler' + Jeweler::Tasks.new do |s| + s.name = "sirb" + s.summary = "Descriptive statistics + IRB + any other useful numerical library you may have around" + s.email = "davidlamontrichards@gmail.com" + s.homepage = "http://github.com/davidrichards/sirb" + s.description = "A series of useful tools that a console should probably have, if your goal is to crunch a few numbers. It includes all the packages that I use, if you have them. Also incorporates some functional style programming and stored procedures to make your console experience even more delightful." + s.authors = ["David Richards"] + end +rescue LoadError + puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" end -Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext } +require 'rake/rdoctask' +Rake::RDocTask.new do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'sirb' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README*') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +require 'rake/testtask' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = false +end + +begin + require 'rcov/rcovtask' + Rcov::RcovTask.new do |t| + t.libs << 'test' + t.test_files = FileList['test/**/*_test.rb'] + t.verbose = true + end +rescue LoadError + puts "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" +end + +begin + require 'cucumber/rake/task' + Cucumber::Rake::Task.new(:features) +rescue LoadError + puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber" +end + +task :default => :test diff --git a/VERSION.yml b/VERSION.yml index 806ee31..f633ed7 100644 --- a/VERSION.yml +++ b/VERSION.yml @@ -1,4 +1,4 @@ --- :major: 0 :minor: 6 -:patch: 4 +:patch: 5 diff --git a/features/sirbs.feature b/features/sirbs.feature new file mode 100644 index 0000000..b908b60 --- /dev/null +++ b/features/sirbs.feature @@ -0,0 +1,9 @@ +Feature: something something + In order to something something + A user something something + something something something + + Scenario: something something + Given inspiration + When I create a sweet new gem + Then everyone should see how awesome I am diff --git a/features/steps/sirbs_steps.rb b/features/steps/sirbs_steps.rb new file mode 100644 index 0000000..e69de29 diff --git a/features/support/env.rb b/features/support/env.rb new file mode 100644 index 0000000..702f7cb --- /dev/null +++ b/features/support/env.rb @@ -0,0 +1,13 @@ +$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib') +require 'sirbs' + +require 'test/unit/assertions' + +require 'test/unit/assertions' + +World do |world| + + world.extend(Test::Unit::Assertions) + + world +end diff --git a/rdoc/classes/Array.html b/rdoc/classes/Array.html new file mode 100644 index 0000000..566b3ac --- /dev/null +++ b/rdoc/classes/Array.html @@ -0,0 +1,170 @@ + + + + +
+Class | +Array | +
In: | +
+
+ lib/overrides/array.rb
+
+ + + lib/sirb.rb + + + |
+
Parent: | ++ Object + | +
include? | +-> | +single_include? | +
Class | +File | +
In: | +
+
+ lib/overrides/file.rb
+
+ + |
+
Parent: | ++ Object + | +
+From setup.rb. Since I only need one method, I‘ve added it here +instead of using the whole setup.rb lib. +
+ ++ # File lib/overrides/file.rb, line 4 + 4: def self.mkdir_p(dirname, prefix = nil) + 5: dirname = prefix + File.expand_path(dirname) if prefix + 6: # Does not check '/', it's too abnormal. + 7: dirs = File.expand_path(dirname).split(%r<(?=/)>) + 8: if /\A[a-z]:\z/i =~ dirs[0] + 9: disk = dirs.shift +10: dirs[0] = disk + dirs[0] +11: end +12: dirs.each_index do |idx| +13: path = dirs[0..idx].join('') +14: Dir.mkdir path unless File.directory?(path) +15: end +16: end ++
Module | +Functional | +
In: | +
+
+ lib/sirb/functional.rb
+
+ + |
+
+This is probably border-line for what O‘Reilly meant for using their +code. I grabbed six methods from The Ruby Programming Language, section +6.8. I want to experiment with how this could change some of my methods. +This module defines methods and operators for functional programming. +
+ ++Apply this function to each element of the specified Enumerable, returning +an array of results. This is the reverse of Enumerable.map. Use | as an +operator alias. Read "|" as "over" or "applied +over". +
++Example: +
++ a = [[1,2],[3,4]] + sum = lambda {|x,y| x+y} + sums = sum|a # => [3,7] ++ +
+ # File lib/sirb/functional.rb, line 16 +16: def apply(enum) +17: enum.respond_to?(:map) ? enum.map(&self) : self.call(enum) +18: end ++
+Return a lambda equivalent to this one with one or more initial arguments +applied. When only a single argument is being specified, the >> alias +may be simpler to use. Example: +
++ product = lambda {|x,y| x*y} + doubler = product >> 2 ++ +
+ # File lib/sirb/functional.rb, line 70 +70: def apply_head(*first) +71: lambda {|*rest| self[*first.concat(rest)]} +72: end ++
+Return a lambda equivalent to this one with one or more final arguments +applied. When only a single argument is being specified, the << alias +may be simpler. Example: +
++ difference = lambda {|x,y| x-y } + decrement = difference << 1 ++ +
+ # File lib/sirb/functional.rb, line 82 +82: def apply_tail(*last) +83: lambda {|*rest| self[*rest.concat(last)]} +84: end ++
+Return a new lambda that computes self[f[args]]. Use * as an operator alias +for compose. Examples, using the * +alias for this method. +
++f = lambda {|x| x*x } g = lambda {|x| x+1 } (f*g)[2] # => 9 (g*f)[2] # +=> 5 +
++def polar(x,y) +
++ [Math.hypot(y,x), Math.atan2(y,x)] ++
+end def cartesian(magnitude, angle) +
++ [magnitude*Math.cos(angle), magnitude*Math.sin(angle)] ++
+end p,c = method :polar, method :cartesian (c*p)[3,4] # => [3,4] +
+ ++ # File lib/sirb/functional.rb, line 52 +52: def compose(f) +53: if self.respond_to?(:arity) && self.arity == 1 +54: lambda {|*args| self[f[*args]] } +55: else +56: lambda {|*args| self[*f[*args]] } +57: end +58: end ++
+Return a new lambda that caches the results of this function and only calls +the function when new arguments are supplied. +
+ ++ # File lib/sirb/functional.rb, line 94 + 94: def memoize + 95: cache = {} # An empty cache. The lambda captures this in its closure. + 96: lambda {|*args| + 97: # notice that the hash key is the entire array of arguments! + 98: unless cache.has_key?(args) # If no cached result for these args + 99: cache[args] = self[*args] # Compute and cache the result +100: end +101: cache[args] # Return result from cache +102: } +103: end ++
+Use this function to "reduce" an enumerable to a single +quantity. This is the inverse of Enumerable.inject. Use <= as an +operator alias. Mnemonic: <= looks like a needle for injections Example: +
++ data = [1,2,3,4] + sum = lambda {|x,y| x+y} + total = sum<=data # => 10 ++ +
+ # File lib/sirb/functional.rb, line 29 +29: def reduce(enum) +30: enum.inject &self +31: end ++
Class | +Loader | +
In: | +
+
+ lib/sirb/loader.rb
+
+ + |
+
Parent: | ++ Object + | +
+ # File lib/sirb/loader.rb, line 7 + 7: def add_lib(name, &block) + 8: name = name.to_s + 9: block ||= lambda{require name} +10: self.libs[name] = self.safe_require(&block) +11: end ++
+ # File lib/sirb/loader.rb, line 13 +13: def all_libs +14: self.libs.keys.sort +15: end ++
+ # File lib/sirb/loader.rb, line 21 +21: def failed_libs +22: self.libs.map {|k, v| k unless v }.compact +23: end ++
+ # File lib/sirb/loader.rb, line 3 +3: def libs +4: @libs ||= {} +5: end ++
+ # File lib/sirb/loader.rb, line 17 +17: def libs_loaded +18: self.libs.map {|k, v| k if v }.compact +19: end ++
+ # File lib/sirb/loader.rb, line 25 +25: def safe_require(&block) +26: begin +27: block.call +28: # If the lib is already loaded, it may return false, but it's available. +29: # If there's a problem with this, you should require the lib in a block +30: # and raise an exception if it fails. +31: true +32: # If the laod returns false, then +33: rescue Exception => e # Very important +34: false +35: end +36: end ++
+ # File lib/sirb/loader.rb, line 38 +38: def to_s +39: return nil if self.failed_libs.empty? +40: "Libs loaded:\n\t" + (self.libs_loaded.empty? ? "None" : self.libs_loaded.join(", ")) + +41: "\nLibs NOT loaded:\n\t" + (self.failed_libs.empty? ? "None" : self.failed_libs.join(", ")) +42: end ++
Class | +Method | +
In: | +
+
+ lib/sirb/functional.rb
+
+ + |
+
Parent: | ++ Object + | +
Class | +Module | +
In: | +
+
+ lib/overrides/module.rb
+
+ + |
+
Parent: | ++ Object + | +
instance_method | +-> | +[] | +
+ | +Access instance methods with array notation. Returns UnboundMethod, + + | +
+Define a instance method with name sym and body f. Example: +String[:backwards] = lambda { reverse } +
+ ++ # File lib/overrides/module.rb, line 59 +59: def []=(sym, f) +60: self.instance_eval { define_method(sym, f) } +61: end ++
+Stolen wholesale out of active_support. I didn‘t want the whole gem +in this gem. +
+ ++ # File lib/overrides/module.rb, line 4 + 4: def alias_method_chain(target, feature) + 5: # Strip out punctuation on predicates or bang methods since + 6: # e.g. target?_without_feature is not a valid method name. + 7: aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1 + 8: yield(aliased_target, punctuation) if block_given? + 9: +10: with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}" +11: +12: alias_method without_method, target +13: alias_method target, with_method +14: +15: case +16: when public_method_defined?(without_method) +17: public target +18: when protected_method_defined?(without_method) +19: protected target +20: when private_method_defined?(without_method) +21: private target +22: end +23: end ++
+Moves a method safely into a new name, only if it exists. The default name +is original_#{method_name}. So, archive_method(:x) creates original_x as a +method, and removes x, so that a module can then use the x method name for +something else. To be honest, I‘m not really sure why I can‘t +create a new method without overriding the old one, it seems like it used +to work, and I may have something boinked in my system, but it‘s late +and I could be wrong too. +
+ ++ # File lib/overrides/module.rb, line 32 +32: def archive_method(target, new_name=nil) +33: new_name ||= ("original_" + target.to_s).to_sym +34: begin +35: alias_method(new_name, target) +36: +37: case +38: when public_method_defined?(target) +39: public new_name +40: when protected_method_defined?(target) +41: protected new_name +42: when private_method_defined?(target) +43: private new_name +44: end +45: +46: remove_method(target) +47: rescue +48: # The instance_methods show that the soon-to-be-defined methods are +49: # defined before they are, really weird. Need to see if this is a +50: # problem with 1.9. For now, fail silently. +51: end +52: end ++
+Only defines the method if it hasn‘t been defined before. It‘s +a call for monkey patching, instead of gorilla patching. +
+ ++ # File lib/overrides/module.rb, line 65 +65: def safe_def(sym, &block) +66: return true if self.instance_methods.include?(sym.to_s) +67: self.[]=(sym, block) +68: end ++
Class | +Proc | +
In: | +
+
+ lib/sirb/functional.rb
+
+ + + lib/sirb/sproc.rb + + + |
+
Parent: | ++ Object + | +
inspect | +-> | +old_inspect | +
source | +[RW] | ++ |
+ # File lib/sirb/sproc.rb, line 192 +192: def self._load(code) +193: self.from_string(code) +194: end ++
+ # File lib/sirb/sproc.rb, line 184 +184: def self.allocate; from_string ""; end ++
+ # File lib/sirb/sproc.rb, line 186 +186: def self.from_string(string) +187: result = eval("proc {#{string}}") +188: result.source = string +189: return result +190: end ++
+ # File lib/sirb/sproc.rb, line 163 +163: def ==(other) +164: if self.source and other.source +165: self.source == other.source +166: else +167: self.id == other.id +168: end +169: end ++
+ # File lib/sirb/sproc.rb, line 171 +171: def _dump(depth = 0) +172: if source +173: source +174: else +175: raise(TypeError, "Can't serialize Proc with unknown source code.") +176: end +177: end ++
+ # File lib/sirb/sproc.rb, line 155 +155: def inspect +156: if source +157: "proc {#{source}}" +158: else +159: old_inspect +160: end +161: end ++
+ # File lib/sirb/sproc.rb, line 149 +149: def source +150: ProcSource.handle(self) unless @source +151: @source +152: end ++
+ # File lib/sirb/sproc.rb, line 141 +141: def source_descriptor +142: if md = /^#<Proc:0x[0-9A-Fa-f]+@(.+):(\d+)>$/.match(old_inspect) +143: filename, line = md.captures +144: return filename, line.to_i +145: end +146: end ++
Module | +ProcSource | +
In: | +
+
+ lib/sirb/sproc.rb
+
+ + |
+
+ # File lib/sirb/sproc.rb, line 18 +18: def get_lines(filename, start_line = 0) +19: case filename +20: # special "(irb)" descriptor? +21: when "(irb)" +22: IRB.conf[:MAIN_CONTEXT].io.line(start_line .. -1) +23: # special "(eval...)" descriptor? +24: when /^\(eval.+\)$/ +25: EVAL_LINES__[filename][start_line .. -1] +26: # regular file +27: else +28: # Ruby already parsed this file? (see disclaimer above) +29: if lines = SCRIPT_LINES__[filename] +30: lines[(start_line - 1) .. -1] +31: # If the file exists we're going to try reading it in +32: elsif File.exist?(filename) +33: begin +34: File.readlines(filename)[(start_line - 1) .. -1] +35: rescue +36: nil +37: end +38: end +39: end +40: end ++
+ # File lib/sirb/sproc.rb, line 42 + 42: def handle(proc) + 43: filename, line = proc.source_descriptor + 44: lines = get_lines(filename, line) || [] + 45: + 46: lexer = RubyLex.new + 47: lexer.set_input(StringIO.new(lines.join)) + 48: + 49: state = :before_constructor + 50: nesting_level = 1 + 51: start_token, end_token = nil, nil + 52: found = false + 53: while token = lexer.token + 54: # we've not yet found any proc-constructor -- we'll try to find one. + 55: if [:before_constructor, :check_more].include?(state) + 56: # checking more and newline? -> done + 57: if token.is_a?(RubyToken::TkNL) and state == :check_more + 58: state = :done + 59: break + 60: end + 61: # token is Proc? + 62: if token.is_a?(RubyToken::TkCONSTANT) and + 63: token.instance_variable_get(:@name) == "Proc" + 64: # method call? + 65: if lexer.token.is_a?(RubyToken::TkDOT) + 66: method = lexer.token + 67: # constructor? + 68: if method.is_a?(RubyToken::TkIDENTIFIER) and + 69: method.instance_variable_get(:@name) == "new" + 70: unless state == :check_more + 71: # okay, code will follow soon. + 72: state = :before_code + 73: else + 74: # multiple procs on one line + 75: return + 76: end + 77: end + 78: end + 79: # token is lambda or proc call? + 80: elsif token.is_a?(RubyToken::TkIDENTIFIER) and + 81: %w{proc lambda}.include?(token.instance_variable_get(:@name)) + 82: unless state == :check_more + 83: # okay, code will follow soon. + 84: state = :before_code + 85: else + 86: # multiple procs on one line + 87: return + 88: end + 89: end + 90: + 91: # we're waiting for the code start to appear. + 92: elsif state == :before_code + 93: if token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO) + 94: # found the code start, update state and remember current token + 95: state = :in_code + 96: start_token = token + 97: end + 98: + 99: # okay, we're inside code +100: elsif state == :in_code +101: if token.is_a?(RubyToken::TkRBRACE) or token.is_a?(RubyToken::TkEND) +102: nesting_level -= 1 +103: if nesting_level == 0 +104: # we're done! +105: end_token = token +106: # parse another time to check if there are multiple procs on one line +107: # we can't handle that case correctly so we return no source code at all +108: state = :check_more +109: end +110: elsif token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO) or +111: token.is_a?(RubyToken::TkBEGIN) or token.is_a?(RubyToken::TkCASE) or +112: token.is_a?(RubyToken::TkCLASS) or token.is_a?(RubyToken::TkDEF) or +113: token.is_a?(RubyToken::TkFOR) or token.is_a?(RubyToken::TkIF) or +114: token.is_a?(RubyToken::TkMODULE) or token.is_a?(RubyToken::TkUNLESS) or +115: token.is_a?(RubyToken::TkUNTIL) or token.is_a?(RubyToken::TkWHILE) or +116: token.is_a?(RubyToken::TklBEGIN) +117: nesting_level += 1 +118: end +119: end +120: end +121: +122: if start_token and end_token +123: start_line, end_line = start_token.line_no - 1, end_token.line_no - 1 +124: source = lines[start_line .. end_line] +125: start_offset = start_token.char_no +126: start_offset += 1 if start_token.is_a?(RubyToken::TkDO) +127: end_offset = -(source.last.length - end_token.char_no) +128: source.first.slice!(0 .. start_offset) +129: source.last.slice!(end_offset .. -1) +130: +131: # Can't use .strip because newline at end of code might be important +132: # (Stuff would break when somebody does proc { ... #foo\n}) +133: proc.source = source.join.gsub(/^ | $/, "") +134: end +135: end ++
Module | +Sirb::CommandLineMethods | +
In: | +
+
+ lib/sirb/runner.rb
+
+ + |
+
+These should be available in the console generally +
+ ++May want to move this to delegate (standard library) www.ruby-doc.org/stdlib/libdoc/delegate/rdoc/index.html +
+ ++ # File lib/sirb/runner.rb, line 225 +225: def method_missing(sym, *args, &block) +226: if Runner.find(sym) +227: Runner.run(sym, *args) +228: elsif Runner.respond_to?(sym) +229: Runner.send(sym, *args, &block) +230: else +231: super +232: end +233: end ++
Module | +Sirb::EnumerableStatistics | +
In: | +
+
+ lib/sirb/enumerable_statistics.rb
+
+ + |
+
+These are the standard R vector functions that I want to add to any +Enumerable class for Ruby. I started by borrowing heavily from +Gotoken’ math/statistics project (raa.ruby-lang.org/project/math-statistics/). +There were a few changes that don‘t make sense in the idiomatic Ruby +that I now use (a few things have changed since 2001). +
++The following is a table of values from R to my methods +
++max | max min | min sum | sum mean | mean median | median range | range var | var variance cor | cor correlation +sort | sort rank | rank order | order quantile | quantile cumsum | cum_sum cumulative_sum cumprod | cum_prod cumulative_product cummax | cum_max cumulative_max cummin | cum_min cumulative_min pmax | p_max +pmin | p_min +
+ +max | +-> | +original_max | +
min | +-> | +original_min | +
+ # File lib/sirb/enumerable_statistics.rb, line 36 +36: def self.append_features(mod) +37: +38: alias :original_max :max +39: alias :original_min :min +40: +41: unless mod < Enumerable +42: raise TypeError, +43: "`#{self}' can't be included non Enumerable (#{mod})" +44: end +45: +46: def mod.default_block= (block) +47: self.const_set("STAT_BLOCK", block) +48: end +49: +50: def mod.default_block +51: defined?(self::STAT_BLOCK) && self::STAT_BLOCK +52: end +53: +54: super +55: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 77 +77: def average(&block) +78: sum(&block)/size +79: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 316 +316: def cum_max(&block) +317: current_max = nil +318: if block_given? +319: map {|i| current_max = Object.max(current_max, yield(i)) } +320: elsif default_block +321: map {|i| current_max = Object.max(current_max, default_block[*i]) } +322: else +323: map {|i| current_max = Object.max(current_max, i) } +324: end +325: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 328 +328: def cum_min(&block) +329: current_min = nil +330: if block_given? +331: map {|i| current_min = Object.min(current_min, yield(i)) } +332: elsif default_block +333: map {|i| current_min = Object.min(current_min, default_block[*i]) } +334: else +335: map {|i| current_min = Object.min(current_min, i) } +336: end +337: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 303 +303: def cum_prod(sorted=false, &block) +304: prod = 1.0 +305: obj = sorted ? self.new_sort : self +306: if block_given? +307: obj.map { |i| prod *= yield(i) } +308: elsif default_block +309: obj.map { |i| prod *= default_block[*i] } +310: else +311: obj.map { |i| prod *= i } +312: end +313: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 290 +290: def cum_sum(sorted=false, &block) +291: sum = 0.0 +292: obj = sorted ? self.new_sort : self +293: if block_given? +294: obj.map { |i| sum += yield(i) } +295: elsif default_block +296: obj.map { |i| sum += default_block[*i] } +297: else +298: obj.map { |i| sum += i } +299: end +300: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 57 +57: def default_blockdefault_blockdefault_block +58: @stat_block || self.class.default_block +59: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 61 +61: def default_block=(block) +62: @stat_block = block +63: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 112 +112: def max +113: list = if block_given? +114: map{|x| yield(x) } +115: elsif default_block +116: map{|x| default_block[*x] } +117: else +118: self +119: end +120: Object.max(*list) +121: end ++
+The slow way is to iterate up to the middle point. A faster way is to use +the index, when available. If a block is supplied, always iterate to the +middle point. +
+ ++ # File lib/sirb/enumerable_statistics.rb, line 126 +126: def median(ratio=0.5, &block) +127: return iterate_midway(ratio, &block) if block_given? +128: begin +129: mid1, mid2 = middle_two +130: sorted = new_sort +131: med1, med2 = sorted[mid1], sorted[mid2] +132: return med1 if med1 == med2 +133: return med1 + ((med2 - med1) * ratio) +134: rescue +135: iterate_midway(ratio, &block) +136: end +137: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 101 +101: def min(&block) +102: list = if block_given? +103: map{|x| yield(x) } +104: elsif default_block +105: map{|x| default_block[*x] } +106: else +107: self +108: end +109: Object.min(*list) +110: end ++
+I don‘t pass the block to the sort, because a sort block needs to +look something like: {|x,y| x <=> y}. To get around this, set the +default block on the object. +
+ ++ # File lib/sirb/enumerable_statistics.rb, line 219 +219: def new_sort(&block) +220: if block_given? +221: map { |i| yield(i) }.sort.dup +222: elsif default_block +223: map { |i| default_block[*i] }.sort.dup +224: else +225: sort().dup +226: end +227: end ++
+Given values like [10,5,5,1] Rank should produce something like [4,2,2,1] +And order should produce +something like [4,2,3,1] The trick is that rank skips as many as were +duplicated, so there could not be a 3 in the rank from the example above. +
+ ++ # File lib/sirb/enumerable_statistics.rb, line 249 +249: def order(&block) +250: hold= [] +251: rank(&block).each_with_index do |x, i| +252: j = i +253: while hold.include?(j) do +254: j += 1 +255: end +256: hold << j +257: end +258: end ++
+First quartile: nth_split_by_m(1, 4) Third quartile: nth_split_by_m(3, 4) +Median: nth_split_by_m(1, 2) Doesn‘t match R, and it‘s silly to +try to. def nth_split_by_m(n, m) +
++ sorted = new_sort + dividers = m - 1 + if size % m == dividers # Divides evenly + # Because we have a 0-based list, we get the floor + i = ((size / m.to_f) * n).floor + j = i + else + # This reflects R's approach, which I don't think I agree with. + i = (((size / m.to_f) * n) - 1) + i = i > (size / m.to_f) ? i.floor : i.ceil + j = i + 1 + end + sorted[i] + ((n / m.to_f) * (sorted[j] - sorted[i])) ++
+end +
+ ++ # File lib/sirb/enumerable_statistics.rb, line 280 +280: def quantile(&block) +281: [ +282: min(&block), +283: first_half(&block).median(0.25, &block), +284: median(&block), +285: second_half(&block).median(0.75, &block), +286: max(&block) +287: ] +288: end ++
+Just an array of [min, max] to comply with R uses of +the work. Use range_as_range if you want a +real Range. +
+ ++ # File lib/sirb/enumerable_statistics.rb, line 199 +199: def range(&block) +200: [min(&block), max(&block)] +201: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 212 +212: def range_as_range(&block) +213: range_class.new(min(&block), max(&block)) +214: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 208 +208: def range_class +209: @range_class ||= Range +210: end ++
+Doesn‘t overwrite things like Matrix#rank +
+ ++ # File lib/sirb/enumerable_statistics.rb, line 230 +230: def rank(&block) +231: +232: sorted = new_sort +233: +234: if block_given? +235: map { |i| sorted.index(yield(i)) + 1 } +236: elsif default_block +237: map { |i| sorted.index(default_block[*i]) + 1 } +238: else +239: map { |i| sorted.index(i) + 1 } +240: end +241: +242: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 96 +96: def standard_deviation(&block) +97: Math::sqrt(variance(&block)) +98: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 65 +65: def sum +66: sum = 0.0 +67: if block_given? +68: each{|i| sum += yield(i)} +69: elsif default_block +70: each{|i| sum += default_block[*i]} +71: else +72: each{|i| sum += i} +73: end +74: sum +75: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 83 +83: def variance(&block) +84: m = mean(&block) +85: sum_of_differences = if block_given? +86: sum{ |i| j=yield(i); (m - j) ** 2 } +87: elsif default_block +88: sum{ |i| j=default_block[*i]; (m - j) ** 2 } +89: else +90: sum{ |i| (m - i) ** 2 } +91: end +92: sum_of_differences / (size - 1) +93: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 151 +151: def first_half(&block) +152: fh = self[0..median_position].dup +153: end ++
+An iterative version of median +
+ ++ # File lib/sirb/enumerable_statistics.rb, line 164 +164: def iterate_midway(ratio, &block) +165: mid1, mid2, last_value, j, sorted, sort1, sort2 = middle_two, nil, 0, new_sort, nil, nil +166: +167: if block_given? +168: sorted.each do |i| +169: last_value = yield(i) +170: j += 1 +171: sort1 = last_value if j == mid1 +172: sort2 = last_value if j == mid2 +173: break if j >= mid2 +174: end +175: elsif default_block +176: sorted.each do |i| +177: last_value = default_block[*i] +178: j += 1 +179: sort1 = last_value if j == mid1 +180: sort2 = last_value if j == mid2 +181: break if j >= mid2 +182: end +183: else +184: sorted.each do |i| +185: last_value = i +186: sort1 = last_value if j == mid1 +187: sort2 = last_value if j == mid2 +188: j += 1 +189: break if j >= mid2 +190: end +191: end +192: return med1 if med1 == med2 +193: return med1 + ((med2 - med1) * ratio) +194: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 146 +146: def median_position +147: middle_two.last +148: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 139 +139: def middle_two +140: mid2 = size.div(2) +141: mid1 = (size % 2 == 0) ? mid2 - 1 : mid2 +142: return mid1, mid2 +143: end ++
+ # File lib/sirb/enumerable_statistics.rb, line 156 +156: def second_half(&block) +157: # Total crap, but it's the way R does things, and this will most likely +158: # only be used to feed R some numbers to plot, if at all. +159: sh = size <= 5 ? self[median_position..-1].dup : self[median_position - 1..-1].dup +160: end ++
Module | +Sirb::GeneralStatistics::InstanceMethods | +
In: | +
+
+ lib/sirb/general_statistics.rb
+
+ + |
+
+Returns the max, the non-nil +value, or nil (if both are nil). A block can be passed if a special +comparison is wanted (not typically). +
+ ++ # File lib/sirb/general_statistics.rb, line 17 +17: def max(*x, &block) +18: return x.first if x.size == 1 +19: return max2(x[0], x[1], &block) if x.size == 2 +20: a = x.first +21: (1...x.size).each { |b| +22: a = max2(a,x[b], &block) } +23: a +24: end ++
+Returns the max, the non-nil +value, or nil (if both are nil). A block can be passed if a special +comparison is wanted (not typically). +
+ ++ # File lib/sirb/general_statistics.rb, line 28 +28: def max2(x,y, &block) +29: return y if x.nil? +30: return x if y.nil? +31: if block_given? +32: yield(x,y) +33: else +34: (x <=> y) > 0 ? x : y +35: end +36: end ++
+ # File lib/sirb/general_statistics.rb, line 38 +38: def min(*x, &block) +39: return x.first if x.size == 1 +40: return min2(x[0], x[1], &block) if x.size == 2 +41: a = x.first +42: (1...x.size).each { |b| +43: a = min2(a,x[b], &block) } +44: a +45: end ++
+Returns the min, the non-nil +value, or nil (if both are nil). A block can be passed if a special +comparison is wanted (not typically). +
+ ++ # File lib/sirb/general_statistics.rb, line 49 +49: def min2(x,y, &block) +50: return y if x.nil? +51: return x if y.nil? +52: if block_given? +53: yield(x,y) +54: else +55: (x <=> y) < 0 ? x : y +56: end +57: end ++
Class | +Sirb::Runner | +
In: | +
+
+ lib/sirb/runner.rb
+
+ + |
+
Parent: | ++ Object + | +
description | +[R] | ++ |
name | +[R] | ++ |
+Needs a lambda, instead of a block, because we‘re serializing this +and the code I use to serialize this needs to be able to find the string that created this code. +
+ ++ # File lib/sirb/runner.rb, line 32 +32: def add_command(name, l, description=nil) +33: coerce name +34: pstore.transaction { pstore[@name] = new(name.to_s, l, description) } +35: end ++
+ # File lib/sirb/runner.rb, line 51 +51: def commands +52: pstore.transaction { pstore.roots } +53: end ++
+ # File lib/sirb/runner.rb, line 60 +60: def find(name) +61: coerce name +62: begin +63: pstore.transaction { pstore[@name] } +64: rescue Exception => e +65: # Don't know a good way to look in the pstore for something that doesn't +66: # exist +67: nil +68: end +69: end ++
+ # File lib/sirb/runner.rb, line 138 +138: def left_justify(str) +139: return "" unless str +140: str.split("\n").map {|line| line.lstrip }.join("\n") +141: end ++
+Needs a lambda, instead of a block, because we‘re serializing this +and the code I use to serialize this needs to be able to find the string that created this code. +
+ ++ # File lib/sirb/runner.rb, line 149 +149: def initialize(name, l, description=nil) +150: @name, @block, @description = name, l, description +151: end ++
+ # File lib/sirb/runner.rb, line 37 +37: def remove_command(name) +38: coerce name +39: pstore.transaction { pstore.delete(@name) } +40: end ++
+Very harsh, but necessary if you have an unknown issue with a stored block. +
+ ++ # File lib/sirb/runner.rb, line 43 +43: def reset_commands +44: puts "Are you sure you want to remove all commands?(yN)\nThis will delete the contents of #{filename}" +45: result = gets.strip +46: return false unless result == 'y' +47: `rm -rf #{filename}` +48: puts "Successfully reset all commands." +49: end ++
+ # File lib/sirb/runner.rb, line 71 +71: def run(name, *data) +72: coerce name +73: raise ArgumentError, "Unknown command: #{name}" unless self.find(@name) +74: self.find(@name).run(*data) +75: end ++
+ # File lib/sirb/runner.rb, line 87 + 87: def sirb_description + 88: + 89: result = left_justify %{ + 90: This is Irb, with some extra libraries and commands loaded. + 91: You have loaded the following libraries: + 92: } + 93: result += tab_indent( Loader.libs_loaded.join("\n") ) + 94: result += "\n\n" + 95: + 96: if not Loader.failed_libs.empty? + 97: result += "The following libraries are not available on your system at this time:\n" + 98: result += tab_indent( Loader.failed_libs.join("\n") ) + 99: result += "\n\n" +100: end +101: +102: result +103: end ++
+ # File lib/sirb/runner.rb, line 82 +82: def sirb_help(command=nil) +83: return puts(command_help(command)) if command +84: puts(sirb_description, all_commands_description, '') +85: end ++
+Probably need to setup some decorators here instead… +
+ ++ # File lib/sirb/runner.rb, line 133 +133: def tab_indent(str) +134: return "" unless str +135: str.strip.split("\n").map {|line| "\t#{line.strip}" }.join("\n") +136: end ++
+ # File lib/sirb/runner.rb, line 105 +105: def all_commands_description +106: result = '' +107: +108: if commands.empty? +109: result += "You have not stored any custom commands at this time.\n\n" +110: else +111: result += "You have setup the following commands:\n\n" +112: result += commands_as_commands.map{ |cmd| cmd.command_help }.join("\n") +113: result += "\n\n" +114: end +115: +116: result += "You can store a command like this:\n" +117: result += tab_indent(%{ +118: set :command_name, lambda{|list params| command }, "Optional description" +119: }) +120: +121: result +122: +123: end ++
+ # File lib/sirb/runner.rb, line 77 +77: def coerce(name) +78: @name = name.to_sym +79: end ++
+ # File lib/sirb/runner.rb, line 126 +126: def command_help(command) +127: cmd = find(command) +128: cmd ? cmd.command_help : "Could not find the command: #{command}" +129: end ++
+ # File lib/sirb/runner.rb, line 55 +55: def commands_as_commands +56: commands.map {|cmd| find(cmd)} +57: end ++
+ # File lib/sirb/runner.rb, line 19 +19: def dirname +20: @@dirname ||= File.expand_path(File.join(ENV['HOME'], '.sirb')) +21: end ++
+ # File lib/sirb/runner.rb, line 24 +24: def filename +25: @@filename ||= File.join(dirname, "proc.pstore") +26: end ++
+ # File lib/sirb/runner.rb, line 172 +172: def command_help +173: result = (self.description and not self.description.empty?) ? formatted_description : empty_description +174: result + source_description +175: end ++
+Kind of loose: allows me to use reduce, map, or just call a stored command. +This may just seem to work, but we‘ll see. +
+ ++ # File lib/sirb/runner.rb, line 155 +155: def run(*data) +156: case @block.arity +157: when 2 +158: # Same as reduce +159: data.inject &@block +160: when 1 +161: # Same as map +162: data.map &@block +163: when -1 +164: # Same as call +165: @block.call +166: else +167: # Probably not going to work. +168: data.inject &@block +169: end +170: end ++
+ # File lib/sirb/runner.rb, line 196 +196: def source_description +197: "\n\tSource: #{@block.source}" +198: end ++
+ # File lib/sirb/runner.rb, line 177 +177: def arity_desc +178: case @block.arity +179: when -1 +180: "takes 0 arguments" +181: when 1 +182: "takes 1 argument, returns an array" +183: when 2 +184: "takes 2 arguments, returns a single value--reduce function" +185: else +186: "takes #{@block.arity} arguments" +187: end +188: end ++
+ # File lib/sirb/runner.rb, line 200 +200: def empty_description +201: "#{name_line}\tThere is no other information available for the #{self.name.to_s} command." +202: end ++
+ # File lib/sirb/runner.rb, line 205 +205: def formatted_description +206: name_line + +207: Runner.tab_indent(self.description) +208: end ++
+ # File lib/sirb/runner.rb, line 191 +191: def name_line +192: "* #{self.name} (#{arity_desc})\n" +193: end ++
Class | +Symbol | +
In: | +
+
+ lib/overrides/symbol.rb
+
+ + |
+
Parent: | ++ Object + | +
+Return the Method of obj named by this symbol. +This may be a singleton method of obj (such as a class method) or an +instance method defined by obj.class or inherited from a superclass. +Examples: +
++ creator = :new[Object] # Class method Object.new + doubler = :*[2] # * method of Fixnum 2 ++ +
+ # File lib/overrides/symbol.rb, line 17 +17: def [](obj) +18: obj.method(self) +19: end ++
+Define a singleton method on object o, using Proc +or Method f as its body. This symbol is used as +the name of the method. Examples: +
++ :singleton[o] = lambda { puts "this is a singleton method of o" } + :class_method[String] = lambda { puts "this is a class method" } ++
+Note that you can‘t create instance methods this way. See Module.[]= +
+ ++ # File lib/overrides/symbol.rb, line 30 +30: def []=(o,f) +31: # We can't use self in the block below, as it is evaluated in the +32: # context of a different object. So we have to assign self to a variable. +33: sym = self +34: # This is the object we define singleton methods on. +35: eigenclass = (class << o; self end) +36: # define_method is private, so we have to use instance_eval to execute it. +37: eigenclass.instance_eval { define_method(sym, f) } +38: end ++
+ # File lib/overrides/symbol.rb, line 2 +2: def to_proc +3: lambda {|receiver, *args| receiver.method(self)[*args]} +4: end ++
Class | +UnboundMethod | +
In: | +
+
+ lib/sirb/unbound_method.rb
+
+ + |
+
Parent: | ++ Object + | +
+Also from The Ruby Programming Language. +
+ +bind | +-> | +[] | +
+ | +Allow [] as an alternative to bind. + + | +
Path: | +README.rdoc + | +
Last Update: | +Mon Feb 23 18:24:59 -0700 2009 | +
+Statistics + IRB. This offers a series of useful tools that a console +should probably have, if your goal is to crunch a few numbers. It includes +all the packages that I use, if you have them. Statisticus will have a +standard library of statistical methods that you may want to have access +to, so it‘s probably worth loading that gem as well. +
++Sirb has a set of rather useful functions setup: +
++For comparing lists, you can use: +
++For other things, you can use: +
++Right now, Sirb attempts to load the following libraries (when available): +
++As you work, you can choose to store a procedure and use it between +sessions. I do this with pstore (part of Ruby‘s standard library) and +some code I found on Ruby Quiz by Florian Groß. The storage process is a +little draconian, so in the short-term, you can call sirb with -w (sirb -w) +and this feature is taken away from the runtime. +
++This is a very useful tool to have if you have some R code that you tend to +use often, say, or some other functions that you discover while working +with some data. +
++From command line: +
++ sirb + >> @a = [3,2,6,7,14] + => [3, 2, 6, 7, 14] + >> @b = [4,6,2,1,19] + => [4, 6, 2, 1, 19] + >> @a.max + => 14 + >> @a.min + => 2 + >> @a.sum + => 32.0 + >> # Most methods have meaningful block semantics + ?> @a.sum {|x| x ** x} + => 1.11120068264282e+16 + >> @a.mean + => 6.4 + >> @a.median + => 6 + >> @a.range + => [2, 14] + >> @a.var + => 22.3 + >> @a.std + => 4.72228758124704 + >> @a.sort + => [2, 3, 6, 7, 14] + >> @a.rank + => [2, 1, 3, 4, 5] + >> @a.order + => [2, 1, 3, 4, 5] + >> @a.quantile + => [2, 3, 6, 7, 14] + >> @a.cum_sum + => [3.0, 5.0, 11.0, 18.0, 32.0] + >> @a.cum_prod + => [3.0, 6.0, 36.0, 252.0, 3528.0] + >> @a.cum_max + => [3, 3, 6, 7, 14] + >> @a.cum_min + => [3, 2, 2, 2, 2] + >> # And some methods between lists + ?> cor(@a,@b) + => 0.755599551729267 + >> # This is the max of each pair (or set) + ?> p_max(@a,@b) + => [4, 6, 6, 7, 19] + >> # These take an arbitrary sized list + ?> p_max(@a,@b, [1,2,3,4,5]) + => [4, 6, 6, 7, 19] + >> # And the min + ?> p_min(@a,@b) + => [3, 2, 2, 1, 14] + >> p_min(@a,@b, [1,2,3,4,5]) + => [1, 2, 2, 1, 5] + >> # Finally, some methods out in the wild for general use: + ?> max(1,2,3,4,5) + => 5 + >> min(1,2,3,4,5) + => 1 + >> product(1,2,3,4,5) + => 120.0 + >> to_pairs(@a,@b) { |a, b| a * b } + => [12, 12, 12, 7, 266] + >> sum_pairs(@a,@b) { |a, b| a * b } + => 309.0 ++
+ [sirb]$: bin/sirb + Loading sirb (Statistics + Irb: 0.6.3) + >> set :add, lambda {|x,y| x + y}, "This is my general-use adder, that adds a set of numbers" + => "add added" + >> add 1, 2, 3 + => 6 + >> sirb_help :add + * add (takes 2 arguments, returns a single value--reduce function) + This is my general-use adder, that adds a set of numbers + Source: |x,y| x + y + => nil + >> sirb_help + + This is Irb, with some extra libraries and commands loaded. + You have loaded the following libraries: + narray + rgl + matrix + rnum + set + statisticus + rbtree + rubygems + command runner + enumerable statistics + general statistics + mathn + + You have setup the following commands: + + * add (takes 2 arguments, returns a single value--reduce function) + This is my general-use adder, that adds a set of numbers + Source: |x,y| x + y + + You can store a command like this: + set :command_name, lambda{|list params| command }, "Optional description" + + >> commands + => [:add] + >> remove_command :add + => #<Sirb::Runner:0x23df728 @block=proc {|x,y| x + y}, @name="add", @description="This is my general-use adder, that adds a set of numbers"> + >> commands + => [] ++
+I‘m generally dissatisfied with quantile, which I think matches the +way that R does their quantile. However, R is inconsistent, and the +community has inconsistent interpretations of how to handle that, so I just +used R as the standard for today. +
++I only mix this into Array right now. I +need to work through the various other classes that I use, and see if this +will mix into all of those: Vector, Matrix, Hash, RBTree, etc. +
++I am a little uncomfortable with my stored procedures library. I want to +review it, because it so aggressively walk on Proc. There are other ideas out there that +I will want to review when I get the chance. +
++(The MIT License) +
++Copyright (c) 2009 David Richards +
++Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +‘Software’), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to the +following conditions: +
++The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +
++THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. +
+ +Path: | +lib/overrides/array.rb + | +
Last Update: | +Sat Feb 21 09:46:25 -0700 2009 | +
Path: | +lib/overrides/file.rb + | +
Last Update: | +Mon Feb 23 14:33:10 -0700 2009 | +
Path: | +lib/overrides/module.rb + | +
Last Update: | +Mon Feb 23 12:35:54 -0700 2009 | +
Path: | +lib/overrides/symbol.rb + | +
Last Update: | +Sat Feb 21 09:46:25 -0700 2009 | +
Path: | +lib/sirb/enumerable_statistics.rb + | +
Last Update: | +Sat Feb 21 09:55:55 -0700 2009 | +
Path: | +lib/sirb/functional.rb + | +
Last Update: | +Sat Feb 21 10:28:51 -0700 2009 | +
+This is probably border-line for what O‘Reilly meant for using their +code. I grabbed six methods from The Ruby Programming Language, section +6.8. I want to experiment with how this could change some of my methods. +This module defines methods and operators for functional programming. +
+ +Path: | +lib/sirb/general_statistics.rb + | +
Last Update: | +Sat Feb 21 10:25:03 -0700 2009 | +
Path: | +lib/sirb/inter_enumerable_statistics.rb + | +
Last Update: | +Sat Feb 21 09:46:25 -0700 2009 | +
Path: | +lib/sirb/loader.rb + | +
Last Update: | +Mon Feb 23 16:47:49 -0700 2009 | +
Path: | +lib/sirb/runner.rb + | +
Last Update: | +Mon Feb 23 18:05:27 -0700 2009 | +
+So, this is interesting. It‘s a class to store operations from +blocks, and run them. +
+ +Path: | +lib/sirb/sproc.rb + | +
Last Update: | +Mon Feb 23 17:36:03 -0700 2009 | +
+This is an adaptation from Florian Groß’s submission to Ruby Quiz +38. rubyquiz.com/quiz38.html +
++I needed a serializable Proc, I +don‘t care about the closure of the proc. I just need to be able to +store procs between Sirb sessions. I‘m using these procs with +explicitly-passed context anyway, so they are meant to be portable +functions. +
+ +SCRIPT_LINES__ | += | +{} unless defined? SCRIPT_LINES__ | ++ | +Tell the ruby interpreter to load code lines of required files into this +filename -> lines Hash. This behaviour seems to be very undocumented and +therefore shouldn‘t really be relied on. + + | +
EVAL_LINES__ | += | +Hash.new | +
eval | +-> | +old_eval | +
+ # File lib/sirb/sproc.rb, line 208 +208: def eval(code, *args) +209: context, descriptor, start_line, *more = *args +210: descriptor ||= "(eval#{code.hash})" +211: start_line ||= 0 +212: lines ||= code.grep(/.*/) +213: EVAL_LINES__[descriptor] ||= Array.new +214: EVAL_LINES__[descriptor][start_line, lines.length] = lines +215: old_eval(code, context, descriptor, start_line, *more) +216: end ++
Path: | +lib/sirb/unbound_method.rb + | +
Last Update: | +Sat Feb 21 09:46:25 -0700 2009 | +
+Also from The Ruby Programming Language. +
+ +Path: | +lib/sirb.rb + | +
Last Update: | +Mon Feb 23 17:37:21 -0700 2009 | +
Path: | +lib/stored_procedures.rb + | +
Last Update: | +Mon Feb 23 17:39:45 -0700 2009 | +
+Called after sirb.rb, when we want to have stored procedures. Sproc/ Proc is pretty draconian, so I want to +pull this out for now until more tests have been done. Probably we‘ll +never want the command runner if Sirb is being used as a library, instead +of as a real Irb instance. +
+ +Path: | +lib/version.rb + | +
Last Update: | +Mon Feb 23 16:48:06 -0700 2009 | +