Do you know the official Ruby interpreter "goruby"?
## ## ## ## ## ## #### ### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######### #### ## ## ######### #### ## #### ## ## ## ## ## ## ## ## ## #### #### ## ## ## #### ## ## ##
If you don’t know what to do on whyday, do not play code golf! It is frustrating! And it is addictive! And you don’t create something useful by golfing!
But it is also fun!
And I am not the only one who wants to squeeze code even more. The original Ruby 1.9 interpreter comes with pretty crazy version of itself: a version for code golfing! Just navigate to the Ruby source and make golf
. All it does is adding about 100 lines of Ruby meta programming :)
Do not miss Object#method_missing
Let’s take look at the main modification and try to understand it line by line (or scroll down, if you just want to know what it does ;) ).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
# code from the Ruby soure, golf_prelude.rb, commented class Object # initialize a global hash cache @@golf_hash = {} # let's begin the madness: our favourite method def method_missing m, *a, &b # search the cache for the missing method/class combination # or assign it with the best matching constant name t = @@golf_hash[ [m,self.class] ] ||= matching_methods(m)[0] # if found in the hash and block_given? if t && b # call the method named by t and pass the arguments __send__(t, *a) {|*args| # also pass found regexes b.binding.eval("proc{|golf_matchdata| $~ = golf_matchdata }").call($~) if $~ # and execute the block b.call(*args) } # if no block is given else # call the method named by t or throw method_missing again t ? __send__(t, *a, &b) : super end end # here comes the magic def matching_methods(s='', m=callable_methods) # build a regex which starts with ^ (beginning) # take every letter of the method_name # insert "(.*?)" instead, which means: match anything and take the smallest match # and insert the letter itself (but escape regex chars) # # for example s='matz' # => /^(.*?)m(.*?)a(.*?)t(.*?)z/ # r=/^#{s.to_s.gsub(/./){"(.*?)"+Regexp.escape($&)}}/ # match all available methods for this regex # and sort them by the least matches of the "fill" regex groups m.grep(r).sort_by do |i| i.to_s.match(r).captures.map(&:size) << i end end # capture not only methods, but also constants def self.const_missing c # search the cache for the missing constant/class combination # or assign it with the best matching method name t = @@golf_hash[ [c,self.class] ] ||= matching_methods(c,constants)[0] # return the constant if it exist t and return const_get(t) # or raise the error raise NameError, "uninitialized constant #{c}", caller(1) end # get the shortest shortcut for a callable method def shortest_abbreviation(s='', m=callable_methods) # check if s (method_name) is capitalized s=s.to_s our_case = (?A..?Z)===s[0] # check if method_name is callable (and get its index in the callable method array) if m.index(s.to_sym) # begin with length 1 and try until method_name's size 1.upto(s.size){|z| # try for all combinations of the method_name letters for the current length # combination: %w[a b c].combination 2 # is [["a","b"],["a","c"],["b","c"]] s.scan(/./).combination(z).map{|trial| # skip if current combination is capitalized, but out method_name not # (and vice versa) next unless ((?A..?Z)===trial[0]) == our_case # join the combination and return it if it is the best matching method name trial*='' return trial if matching_methods(trial,m)[0].to_s==s }} # method_name not found else nil end end # get *all* callable methods of an Object def callable_methods self.class == Object ? methods + private_methods : methods end # ... end
By “abusing” method_missing
and const_missing
for Object
, it lets you call methods and constants with the shortest name possible (e.g: [[1,2,3]].fle
means [[1,2,3]].flatten
). To get the shortest name of a method for an object, you can run for example: "an object".shortest_abbreviation :reverse # => 'rv'
List of further small changes
do_while(&block)
anddo_until(block)
methods which return 0 as long/until the block is true.String
modifications- Alias:
String#split
—>String#/
- String#to_a is done via
String#split('')
- It adds
Array
methods toString
which operate on each char
- Alias:
Enumerator
also usesArray
methods that do not exist forEnumerator
by converting its instance to anArray
- Alias:
puts
—>say
Array#to_s
viaArray#join
FalseClass#to_s
is''
(instead offalse
)Integer
includesEnumerable
viatimes
- The most important method
h
, which [try it out] ;)
GoRuby
Navigate to your Ruby 1.9 source and run make golf
To see goruby in action, checkout shinhs golf server and inspect a problem that is labeled with “post mortem”!