From: "Jörg W Mittag" Date: 2010-11-12T23:15:17+09:00 Subject: Re: Scheme's (cond ((assertion) (value))...(else (value))) statement implemented in ruby? timr wrote: > Hi Rubyists, > I love ruby, but am learning scheme and kinda like the (cond > ((assertion) (value))...(else (value))) syntax I am a little bit confused by your question. In the subject line you are asking about a Ruby equivalent to Scheme's 'cond' statement. But AFAIK, Scheme doesn't *have* statements, it only has epxressions and definitions. If you are looking for an equivalent of the 'cond' derived conditional expression, then Ruby has something which is exactly equivalent (modulo the guarantees about proper tail calls) to the specification in clause 11.4.5 of the 6th Revised Report on the Algorithmic Language Scheme: the "'case' expression without expression" (which is quite a mouthful ...) case when then when else end > for what would normally > look like this in ruby: > > if (assertion 1) > value 1 > elsif (assertion 2) > value 2 > else > default > end > > I find scheme's cond syntax more quickly digestible since it reads > like a table. > (cond > ((assertion 1) (value 1)) > ((assertion 2) (value 2)) > (else (default))) That's equivalent (again, modulo tail context guarantees) to case when assertion 1 then value 1 when assertion 2 then value 2 else default end > So I wondered if I could get a similar looking function in ruby. Here > is an attempt: > > class Object > alias l lambda > > def cond(tests) > #acts similar to scheme/LISP cond function using hash > #key => value rather than scheme's (cond (assertion)(value)... > (else(value))) > tests.each_pair do |k,v| > if k.call > v.call > break > else > next > end > end > end > > def end_cond; l{true}; end > end > > cond l{3 < 2} => l{ puts "3 is less than 2"}, > l{3 > 3} => l{ puts "3 is greater than 3"}, > l{3+1 > 4} => l{ puts "4 is greater than 4"}, > end_cond => l{ puts "hey, it works!"} > > # >> hey, it works! AFAICS, this *does not* behave like either Scheme's 'cond' or Ruby's 'case' expression without expression. In particular, it doesn't seem to return the value of the evaluated consequence expression. Instead, it simply returns the hash that was passed in, which doesn't seem particularly useful to me. This seems to be closer to what you are looking for: class Object private def cond(conds) conds.each {|t, e| if t.is_a?(Proc) then t.() else t end and return e.() } end end puts cond ->{ 3 < 2 } => ->{ '3 is less than 2' }, ->{ 3 > 3 } => ->{ '3 is greater than 3' }, ->{ 3+1 > 4 } => ->{ '4 is greater than 4' }, else: ->{ 'hey, it works!' } > I was surprised that the hash seems not to mix-up the order of the k:v > pairs like it would if the keys were strings or symbols. I suppose > because they are lambda's, the hash doesn't bother alphabetizing them > during for the .each_pair method. Since they stay ordered, this table- > like if;elsif;else seems to work fine. Can anyone think of any issues > to using this approach? Can it give rise to unpredicted values--due to > some behind the curtain hash key sorting? In Ruby 1.9, hashes are always guaranteed to be ordered by insertion. In Ruby 1.8, hashes are always unordered, regardless of whether their keys are symbols, strings, procs or unicorns. > Or can anyone come up with a cleaner syntax than a hash of lambdas for > both keys and values? In Ruby 1.9, I think it is just fine. In Ruby 1.8, you would have to use something which is ordered, like an array, or you would have to require that the branches are mutually exclusive, which of course rules out the possiblity of an 'else' branch. What I am not entirely clear about is what's wrong with puts case when 3 < 2 then '3 is less than 2' when 3 > 3 then '3 is greater than 3' when 3+1 > 4 then '4 is greater than 4' else 'hey, it works!' end That looks just fine to me. Another thing that bugs me is that Ruby is an object-oriented language, but I don't see an obvious object that the cond method should belong to. jwm