On Nov 12, 6:14=A0am, J=F6rg W Mittag <JoergWMittag+R... / GoogleMail.Com>
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 ...)
>
> =A0 =A0 case
> =A0 =A0 when <test> then <expression>
> =A0 =A0 when <test>
> =A0 =A0 =A0 <expression>
> =A0 =A0 =A0 <expression>
> =A0 =A0 else
> =A0 =A0 =A0 <expression>
> =A0 =A0 end
>
>
>
>
>
>
>
>
>
> > for what would normally
> > look like this in ruby:
>
> > if (assertion 1)
> > =A0 value 1
> > elsif (assertion 2)
> > =A0 value 2
> > else
> > =A0 default
> > end
>
> > I find scheme's cond syntax more quickly digestible since it reads
> > like a table.
> > (cond
> > =A0 =A0 ((assertion 1) (value 1))
> > =A0 =A0 ((assertion 2) (value 2))
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (else (default)))
>
> That's equivalent (again, modulo tail context guarantees) to
>
> =A0 =A0 case
> =A0 =A0 when assertion 1 then value 1
> =A0 =A0 when assertion 2 then value 2
> =A0 =A0 else =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0default
> =A0 =A0 end
>
>
>
>
>
>
>
>
>
> > So I wondered if I could get a similar looking function in ruby. Here
> > is an attempt:
>
> > class Object
> > =A0 alias l lambda
>
> > =A0 def cond(tests)
> > =A0 =A0 #acts similar to scheme/LISP cond function using hash
> > =A0 =A0 #key =3D> value rather than scheme's (cond (assertion)(value)..=
.
> > (else(value)))
> > =A0 =A0 tests.each_pair do |k,v|
> > =A0 =A0 =A0 if k.call
> > =A0 =A0 =A0 =A0 v.call
> > =A0 =A0 =A0 =A0 break
> > =A0 =A0 =A0 else
> > =A0 =A0 =A0 =A0 next
> > =A0 =A0 =A0 end
> > =A0 =A0 end
> > =A0 end
>
> > =A0 def end_cond; l{true}; end
> > end
>
> > cond =A0l{3 < 2} =A0 =A0 =A0=3D> =A0l{ puts "3 is less than 2"},
> > =A0 =A0 =A0 =A0 =A0l{3 > 3} =A0 =A0 =A0=3D> =A0l{ puts "3 is greater th=
an 3"},
> > =A0 =A0 =A0 =A0 =A0l{3+1 > 4} =A0=3D> =A0l{ puts "4 is greater than 4"}=
,
> > =A0 =A0 =A0 =A0end_cond =A0 =A0=3D> =A0l{ 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:
>
> =A0 =A0 class Object
> =A0 =A0 =A0 private
>
> =A0 =A0 =A0 def cond(conds)
> =A0 =A0 =A0 =A0 conds.each {|t, e| if t.is_a?(Proc) then t.() else t end =
and return e.() }
> =A0 =A0 =A0 end
> =A0 =A0 end
>
> =A0 =A0 puts cond ->{ =A03 =A0< 2 } =3D> ->{ '3 is less than 2' },
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 ->{ =A03 =A0> 3 } =3D> ->{ '3 is greater than=
 3' },
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 ->{ 3+1 > 4 } =3D> ->{ '4 is greater than 4' =
},
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else: ->{ 'hey, it wor=
ks!' }
>
> > 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.
>
But it runs fine for me in 1.8.7:
>> RUBY_VERSION
=3D> "1.8.7"
>> class Object
>>   alias l lambda
>>
?>   def cond(tests)
>>     #acts similar to scheme/LISP cond function using hash
?>     #key =3D> 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
=3D> nil
>>
?> cond  l{3 < 2}    =3D>  l{ puts "3 is greater than 2"},
?>       l{3 > 3}    =3D>  l{ puts "3 is greater than 3"},
?>       l{3+1 > 4}  =3D>  l{ puts "4 is greater than 4"},
?>       end_cond    =3D>  l{ puts "hey, it works!"}
hey, it works!
=3D> nil


> What I am not entirely clear about is what's wrong with
>
> =A0 =A0 puts case
> =A0 =A0 =A0 =A0 =A0when =A03 =A0< 2 then '3 is less than 2'
> =A0 =A0 =A0 =A0 =A0when =A03 =A0> 3 then '3 is greater than 3'
> =A0 =A0 =A0 =A0 =A0when 3+1 > 4 then '4 is greater than 4'
> =A0 =A0 =A0 =A0 =A0else =A0 =A0 =A0 =A0 =A0 =A0 =A0'hey, it works!'
> =A0 =A0 =A0 =A0 =A0end
>
> That looks just fine to me.
>
Yes. I agree it is beter. I didn't realize that case statements could
be used with this more flexible syntax. My bad.

> 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.
>

If defined in Object class, cond could be used within almost any
context (since almost every object is an ancestor of Object).

> jwm