From: timr Date: 2010-11-13T04:15:15+09:00 Subject: Re: Scheme's (cond ((assertion) (value))...(else (value))) statement implemented in ruby? On Nov 12, 6:14 am, Jörg W Mittag wrote: > 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 Perhaps you didn't notice the beginning of the code, where you find the def for cond within the Object class. 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