The three rules of Ruby Q.:
-
Please do not post any solutions or spoiler discussion for this quiz
until
48 hours have passed from the time on this message. -
Support Ruby Q. by submitting ideas as often as you can:
- Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby T. follow the discussion. Please reply to the original quiz
message,
if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
by Ken B.
S-expressions are a useful way of representing functional expressions in
many
aspects of computing. Lisp’s syntax is based heavily on s-expressions,
and the
fact that Lisp uses them to represent both code and data allows many
interesting
libraries (such as CLSQL: http://clsql.b9.com/) which do things with
functions
besides simply evaluating them. While working on building a SQL
generation
library, I found that it would be nice to be able to generate
s-expressions
programmatically with Ruby.
An s-expression is a nested list structure where the first element of
each list
is the name of the function to be called, and the remaining elements of
the list
are the arguments to that function. (Binary operators are converted to
prefix
notation). For example the s-expression (in LISP syntax)
(max (count field))
would correspond to
max(count(field))
in ordinary functional notation. Likewise,
(roots x (+ (+ (* x x) x) 1 ))
would correspond to
roots(x, ((x*x) + x) + 1)
since we treat binary operators by converting them to prefix notation.
Your mission: Create a function named sxp() that can take a block (not a
string), and create an s-expression representing the code in the block.
Since my goal is to post-process the s-expressions to create SQL code,
there is
some special behavior that I will allow to make this easier. If your
code
evaluates (rather than parsing) purely numerical expressions that don’t
contain
functions or field names (represented by Symbols here), then this is
satisfactory behavior since it shouldn’t matter whether Ruby evaluates
them or
the SQL database evaluates them. This means, for example, that sxp{3+5}
can give
you 8 as an s-expression, but for extra credit, try to eliminate this
behavior
as well and return [:+, 3, 5].
It is very important to avoid breaking the normal semantics of Ruby when
used
outside of a code block being passed to sxp.
Here are some examples and their expected result:
sxp{max(count(:name))} => [:max, [:count, :name]]
sxp{count(3+7)} => [:count, 10] or [:count, [:+, 3, 7]]
sxp{3+:symbol} => [:+, 3, :symbol]
sxp{3+count(:field)} => [:+, 3, [:count, :field]]
sxp{7/:field} => [:/, 7, :field]
sxp{:field > 5} => [:>, :field, 5]
sxp{8} => 8
sxp{:field1 == :field2} => [:==, :field1, :field2]
7/:field => throws TypeError
7+count(:field) => throws NoMethodError
5+6 => 11
:field > 5 => throws NoMethodError
(In code for this concept, I returned my s-expression as an object which
had
inspect() modified to appear as an array. You may return any convenient
object
representation of an s-expression.)