module Enumerable
def concat_map(&f)
inject([]) { |a, b| a.concat(f.call(b)) }
end
end
def guard( b )
if b
[nil]
else
[]
end
end
def r( v )
[v]
end
def comp( s, b = binding )
before_bar, after_bar = s.split("|")
before_bar.gsub!(/\A\s*\[/, '')
after_bar.gsub!(/\]\s*\z/, '')
components = after_bar.split(/;/)
gen_exprs, guard_exprs = components.partition { |e| e =~ /<-/ }
final = "r(#{before_bar})"
final = guard_exprs.reverse.inject(final) { |s, e| "guard(#{e}).concat_map { #{s} }" }
final = gen_exprs.reverse.inject(final) { |s, e|
var, expr = e.split("<-").map { |c| c.strip }
"(#{expr}).concat_map { |#{var}| #{s} }"
}
eval(final,b)
#final
end
See, easy?
Now we can do things like:
def factors( n )
comp "[ [x,y] | x <- (1..n) ; y <- (1..n) ; x * y == n ]", binding
end
If we do factors 25 we get [[1, 25], [5, 5], [25, 1]].
We can also write "quicksort" (being that not so great list comprehension quicksort that I'm sure you've seen before:"
def qsort( a )
if a.length < 2
a
else
pivot = a.first
tail = a[1..-1]
b = binding
qsort(comp("[ x | x <- tail ; x < pivot ]", b)) + [pivot] + qsort(comp("[ y | y <- tail ; y >= pivot ]", b))
end
end
And of course we write a cartesian product function:
def cart_prod(a, b)
comp "[ [x,y] | x <- a ; y <- b ]", binding
end
By the way the code that this expands to is:
(a).concat_map { |x| (b).concat_map { |y| r( [x,y] ) } }
Of course we could've written all these functions before, w/o list comprehensions but this was more fun.
No comments:
Post a Comment