Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Tuesday, March 4, 2008

Approximate Ruby Programming

What if your programming language interpreter didn't mind small spelling mistakes. Imagine that you type a method name a wee bit wrong, but the the interpreter seems to read your mind and call the correct method instead of throwing an NoMethodFound exception at you.

I've been playing a bit with this idea and found a simple but naive way this in Ruby. If you try to call a method that doesn't exist, then the method_missing method gets called. If this method_missing in turn figured out what method you really meant to call and called that instead, you where in the clear? Of course, programs can't read your mind, but a simple way of approximate this is to find the existing method with the shortest edit distance to the misspelled method and call that instead. This is pretty naive, but it will work in many cases and serve as a simple baseline. And it has a very straight-forward implementation in Ruby. Say hello to ... drumroll, please ... approximatize!.

class Example
def test(str)
puts "test method called: #{str}\n"
end
end

approximatize(Example)

ex = Example.new
ex.test "a normal method call"
ex.text "Did you mean test?"
ex.tes "Did you mean test? (then you forgot a letter)"
ex.ttest "Did you mean test? (then wrote a letter to much)"
ex.and_now_for_something_completely_different

This example illustrates a simple use of approximatize. In both all cases, but the last, the test method gets called even though the method name was misspelled. However, the are no methods whose spelling resembles the last call, and thus a NoMethodError is thrown. It's possible to adjust how much error to allow, but it's recommended to keep the max_edit_distance low.

The implementation of approximatize:

def approximatize(target, max_edit_distance = 1)
target = target.class unless target.class == Class

target.class_eval do
define_method :method_missing do |*args|
meth = args.shift
similar_methods = {}

self.methods.each do |m|
dist = m.edit_distance(meth.to_s)
if dist <= max_edit_distance then
if similar_methods[dist].nil? then
similar_methods[dist] = [ m.to_s ]
else
similar_methods[dist] << m.to_s
end
end
end

# Eliminate candidates with higher edit distances than the candidates with the lowest
similar_methods = similar_methods.min.pop unless similar_methods.min.nil?

# Call method only if there is _exactly_ one element with the minimum edit distance
if similar_methods.nil? or similar_methods.size != 1 then
raise NoMethodError.new("undefined method ‘#{meth.to_s}’ for #{self}",meth,args)
else
self.__send__(similar_methods.first,*args)
end
end
end
end

Approximatize defines an method_missing method on the target class. When invoked this method runs through all the methods of the target class and calculates the edit distance of the method. It then selects the method with the lowest edit distance and invokes it (assuming there is only one with such a low edit distance and the this edit distance is lower than the allowed threshold). If no such method can be found, it will throw a NoSuchMethod exception, as would normally have happened when you call a non-existing method.

The dynamic programming version of the edit distance algorithm is implemented directly on the String class. The running time is O(m*n) so it's feasible (polynomial) even though it is executed for each method in the target class. In practice, this doesn't seem to be a problem, since method names tend to be rather short. However, it would probably be a good idea to cache the result of the calculations, instead doing them each time a non-existing method gets called.

class String
def edit_distance(other)
m = []

# create base case entries:
0.upto(size) { |i| m[i] = []; m[i][0] = i }
0.upto(other.size) { |j| m[0][j] = j }

# Fill out the rest of the matrix
1.upto size do |i|
1.upto other.size do |j|
etj = (self[i-1] == other[j-1])?0:1
m[i][j] = [ m[i-1][j-1] + etj , m[i][j-1]+1, m[i-1][j]+1 ].min
end
end

m[size][other.size]
end
end
If you're the type who likes to live life dangerously and are not afraid to break things, why not approximatize your entire Ruby environment?
def approximatize_world(max_edit_distance=1)
ObjectSpace.each_object(Class) do |clazz|
approximatize_class(clazz,max_edit_distance)
end
end


But there are some obvious problems with the concept and the approach:

  • Expect the unexpected: Sometimes the wrong method gets called. One obviously dangerous case springs to mind: The way Ruby has destructive methods ending with an exclamation mark; an edit distance of one from the original name. As such, edit distance is not very clever, and it's really sensitive when it comes to short method names. It should be possible to come up with something better, but until then, edit distance serves as reasonable baseline approximation.
  • It's possible to get a list of defined methods of a class, but these doesn't include aliases for methods or methods implemented using method_missing. Actually, using approximatize on a class might break it's functionality if it depends on method_missing. This can probably be fixed with some clever aliasing though. However, using method_missing like this is asking for trouble.
  • Of course, the approach only handles method names. Syntax errors still cause the interpreter to complain like a "strict old aunt", even though it was perfectly clear what I meant ;-) I would be nice with approximate syntax, but that would require a different kind of Ruby parser
Ahh well, so it isn't very useful in practice, but the idea seems worthwhile, doesn't it? Even if it isn't very useful it is a working prototype illustrating an interesting concept (imho). And more importantly it provided me with a couple of hours of fun :-)

Friday, June 8, 2007

Article about McCarthy's Ambiguous Operator in Ruby

I just discovered an article about an implementation of McCarthy's amb operator in Ruby, written by Erid Kidd. What a gem, it's a beautiful construction.

It uses continuations to implement backtracking and provides a straight-forward way of representing constraint satisfaction problems in Ruby.

Find the details, implementation and article here: www.randomhacks.net.
It's also the subject of a Ruby Quiz. Here is an other implementation in scheme with a detailed description.

Sunday, May 13, 2007

Colorless green ideas sleep furiously: Fun with a Ruby ChomskyBot

I recently fell over the concept of a Chomsky bot. A funny little thing which generates random paragraphs of text from a set sentence building blocks. It combines four kinds of phrases (introduction phrases, subject phrases, verb phrases and object phrases) into a sentence. The sentences this simple construction can create are amazing. They are syntactically correct and "hovers on the edge on understandability".

By the way, the title of this post "Colorless green ideas sleep furiously" is a syntactically correct but nonsensical sentence devised by Noam Chomsky. Noam Chomsky pioneered the field of generative grammars. The ChomskyBot implements a simple generative grammar.

The sentences generated by the bot are similar to the language of Noam Chomsky's works, and I guess the pun is intended.

Of course, I couldn't resist the temptation to write a Ruby version of the Chomsky bot:


class ChomskyBot
@@phrase_elems = [ "intro", "subject", "verb", "object" ]

def initialize(intro_file, subject_file, verb_file, object_file)
@@phrase_elems.each do |e|
instance_eval("@#{e}s = []")
instance_eval("File.open(#{e}_file).each_line" +
"{ |l| @#{e}s.push l.chop }")
end
end

def generate_lines(n)
lines = []
n.times do
@@phrase_elems.each do |e|
eval("lines << @#{e}s.slice!(rand(@#{e}s.size-1)) << ' '")
end
end
lines
end

def paragraph
generate_lines(5).join
end
end



I tried to make it as simple as I could get away with. I shaved quite a few lines using eval, hope it doesn't hurt readability to much.

You'll need some phrase files to play with it. You can find those here:

Introduction phrases
Subject phrases
Verb phrases
Object sentences


You can try the original version of the ChomskyBot online. It's written in Perl (source) by Kevin McGovan. For more information, pay a visit to Chomsky bot inventor John Lawlers website.

Wednesday, May 2, 2007

Transmoglyphing textual logic expression into Latex math

Recently I've been doing some exercises which included a lot of logic expressions. Writing those in Latex becomes really tedious after a while, so I got sidetracked and wrote a small ruby program :-) It transforms a textual logic expression to the Latex equivalent.

For instance, this is a textual of a common logic function (can you see which one?):
(x and not y) or (!x && y)

It translates into latex math:
$(x \wedge \neg y) \vee ( \neg x \wedge y)$

When rendered it looks like this:


Other things like implication, biiimplication and entailment are also supported. The syntax allows a certain degree of freedom in choice of textual logic operators.

The code:


#!/usr/bin/env ruby
# Encode text with logic expressions as a latex math expression

symbols = {
'\wedge' => [ 'and', '&&' ],
'\vee' => [ 'or', '||' ],
'\neg' => [ '^', '!', 'not' ],
'\Leftrightarrow' => [ '<=>', '<->' ],
'\Rightarrow' => [ '=>', '->' ],
'\models' => [ ':-', ':=', 'entails' ]
}

match_exp = {}
symbols.keys.each { |k| symbols[k].each { |re| match_exp[re] = k } }

# Sort string by their length so that longest regexps are matched first

class String
def <=>(other)
other.length <=> length
end
end

loop do
puts "Enter logic text:"
text = $stdin.gets.chomp
break if text == "quit"

match_exp.keys.reverse.each { |k| text.gsub!(k, " #{match_exp[k]} ") }
puts "Latex math expr: $#{text.chomp}$"
end

Tuesday, May 1, 2007

Generate and Test in Ruby

The Generate and Test algorithm (GT) is without comparison the simplest and most inefficient way of solving constraints. Actually, it's not useful for anything but very, very small problems. But it is serves as a nice little illustrating of the concept of constraint solving. Just for fun I wrote a very small GT constraint solver in Ruby the other day, that I decided sharing.

Here is a small toy problem for it to solve and a demonstration of how it works:

  1. gt = GT.new
  2. gt.add_var("a", 0..4)
  3. gt.add_var("b", 0..4)
  4. gt.add_var("c", 0..4)
  5. gt.add_constraint("a % 3 == 0")
  6. gt.add_constraint("b + a < c")
  7. gt.add_constraint("c-a > b")
  8. gt.solve



Running this code will print:

Solutions:
{"a"=>0, "b"=>0, "c"=>1}
{"a"=>0, "b"=>0, "c"=>2}
{"a"=>0, "b"=>0, "c"=>3}
{"a"=>0, "b"=>0, "c"=>4}
{"a"=>0, "b"=>1, "c"=>2}
{"a"=>0, "b"=>1, "c"=>3}
{"a"=>0, "b"=>1, "c"=>4}
{"a"=>0, "b"=>2, "c"=>3}
{"a"=>0, "b"=>2, "c"=>4}
{"a"=>0, "b"=>3, "c"=>4}
{"a"=>3, "b"=>0, "c"=>4}


The implementation:


class GT
def initialize
@variables = Hash.new
@constraints = Array.new
end

def add_var(varname, domain)
@variables[varname] = domain
end

def add_constraint(constraint)
constraint.freeze
@constraints << constraint
end

def generate
gen(@variables, Hash.new,nil)
end

def gen(variables, partial_assignment, solutions)
solutions = Array.new if solutions.nil?

if variables.empty?
if test(partial_assignment)
solutions << partial_assignment.clone
end
return nil # termination
end

# pick the first available variable:
vars = variables.clone
var_name = variables.keys.first
domain = vars[var_name]
vars.delete(var_name)

# Loop over each variable in domain
domain.each do |value|
partial_assignment[var_name] = value
gen(vars, partial_assignment, solutions)
end
solutions
end

def test(assignment)
@constraints.each do |constraint|
c = String.new(constraint)
assignment.each do |key,val|
c.gsub!(key,val.to_s)
end
result = instance_eval(c)
return false if result == false
end
end

def solve
puts "Solutions:"
solutions = generate
solutions.each do |s|
pp s
end

end
end







It is not useful for solving anything interesting though. I tried to make it solve the the send more money puzzle. But this poor algorithm searches it self to death in vain. It took so long, that it was never allowed to terminate...

[ "s", "e", "n", "d", "m", "o", "r", "n", "y" ].each { |var| gt.add_var(var, 0..9) }

gt.add_constraint("m != 0")
gt.add_constraint("s != 0")
gt.add_constraint("m < 3")
gt.add_constraint("(1000*s + 100*e + 10*n + d + 1000*m + 100*o + 10*r + e) == (10000*m + 1000*o + 100*n + 10*e + y)")

gt.solve


Update:Also heck out amb

Sunday, April 22, 2007

My answer to ruby quiz 121

My solution to Ruby quiz 121.

Given some morse code without breaks between letters (which can have ambiguous interpretations), it will generate the words that the morse code can generate.

It's implemented as a recursive depth-first search in Ruby. Branches are expanded dynamically in the first_letters function.


require 'pp'

class Morse
@@alpha = {
"a" => ".-",
"b" => "-...",
"c" => "-.-.",
"d" => "-..",
"e" => ".",
"f" => "..-.",
"g" => "--.",
"h" => "....",
"i" => "..",
"j" => ".---",
"k" => "-.-",
"l" => ".-..",
"m" => "--",
"o" => "---",
"p" => ".--.",
"q" => "--.-",
"r" => ".-.",
"s" => "...",
"t" => "-",
"u" => "..-",
"v" => "...-",
"w" => ".--",
"x" => "-..-",
"y" => "-.--",
"z" => "--.."
}

def initialize
# turn around hash index to use morse chars index
@rev = {}
@@alpha.each { |k,v| @rev[v] = k.to_s }
end

# Returns all letters matching the morse str at this pos
def first_letters(morse, pos)
letters = []
@rev.keys.each do |k|
letters << k unless morse[pos..-1].scan(/^#{k.gsub(".","\\.")}.*/).empty?
end
letters
end

# Returns an array of words that matches 'morse' string
# It's basically a recursive function implementing depth-first search
def morse2words(morse, pos = 0 , seen = "")
solutions = []
first_letters(morse, pos).each do |l|
if morse.length == pos + l.length
solutions << "#{seen}#{@rev[l]}"
else
result = morse2words(morse,(pos+l.length),"#{seen}#{@rev[l]}")
solutions += result
end
end

solutions
end

# Converts a word to a morse string, used for testing
def word2morse(word)
morse = ""
word.each_byte { |b| morse << @@alpha[b.chr] }
morse
end
end


######################
# Test:

def test_word2morse
m = Morse.new
raise unless m.word2morse("sofia") == "...---..-....-"
end

def test_first_letters
m = Morse.new
raise unless m.first_letters(".", 0) == [ "." ];
raise unless m.first_letters("--.--..--.-.", 0) == ["--", "-", "--.", "--.-"]
end

def test_morse2words
m = Morse.new
sofia = "...---..-....-"
solutions = m.morse2words(sofia)
pp solutions
solutions.each do |s|
if m.word2morse(s) != sofia
puts "bad solution: #{s}"
puts "yields #{m.word2morse(s)} in morse"
raise
end
end
end

test_word2morse
test_first_letters
test_morse2words

substr in Ruby

Some times you want to extract the characters from a certain offset within a string. In Perl and PHP you have the substr function, e.g.


$str = "Hello world";
substr($str, 6); # --> "world"


In Ruby, this is done using the slice method of the String class. However, slice works a little different; slice is really an alias to [], so:


str = "Hello world"
str.slice(6) # --> 119
str[6] # --> 119

This may come as a surprise if you are used to substr; this returns the value of the character and not the rest of the string. Instead, we can do the same using negative indices, since negative indices count from the end of the string:


str.slice(6..-1) # --> "world"
str[6..-1] # --> "world"