Scheme's (cond ((assertion) (value))...(else (value))) statementimplemented in ruby?

T

timr

Hi Rubyists,
I love ruby, but am learning scheme and kinda like the (cond
((assertion) (value))...(else (value))) syntax 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)))


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!

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? Or can anyone come up with a
cleaner syntax than a hash of lambdas for both keys and values? I know
this is not idiomatic ruby here. I am just having fun.
 
C

Clifford Heath

timr said:
Hi Rubyists,
I love ruby, but am learning scheme and kinda like the (cond
((assertion) (value))...(else (value))) syntax 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)))

What's wrong with this?

x = 3
case
when x == 2; puts 'two'
when x == 3; puts 'three'
else puts 'other'
end

Clifford Heath.
 
T

timr

What's wrong with this?

x = 3
case
when x == 2; puts 'two'
when x == 3; puts 'three'
else puts 'other'
end

Clifford Heath.

Case interrogates a single variable for its state--whether it falls in
a certain range, or is equal to such and such a value. The cond
statement is much more flexible, in that each assertion can be
completely unrelated to the other assertions--i.e. they can cover more
than the state of a single variable.

cond l{Store.open_time..Store.close_time === Time.now}
=>l{Store.access = true},
l{after_hours_users.include? User.name}
=>l{Store.access = true},
cond_end
=>l{Store.access = false; User.rain_check(1)}

In the above code access is given to anyone during store hours, but if
it is not during store hours and the user is listed as an
after_hours_user, they are allowed acces. If they can't get in because
it is after hours and they are not listed as an after_hours_user, they
are denied access but given a rain_check.

You can't do this kind of complex functionality with case, which only
inquires with regards to one variable.
Tim
 
J

John W Higgins

[Note: parts of this message were removed to make it a legal post.]

Good Morning,

Case interrogates a single variable for its state--whether it falls in
a certain range, or is equal to such and such a value. The cond
statement is much more flexible, in that each assertion can be
completely unrelated to the other assertions--i.e. they can cover more
than the state of a single variable.

Nope, well sort of nope - you are right that it checks against a single
variable but you are missing the trick. However the default "variable" for
case is true and there are lots of options outside of the default as well.

As an example

x = 1
y = 2
case
when x == 2 then puts 'x'
when y == 2 then puts 'y'
end

That outputs a nice shiny "y"

or

x = 1
y = 2
case false
when x == 2 then puts 'x'
when y == 2 then puts 'y'
end

This outputs an even shiner "x"

And even nicer

x = 1
y = 2
case 2
when x then puts 'x'
when y then puts 'y'
end

A nice shiny 'y' pops up on the screen

John
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

Hi Rubyists,
I love ruby, but am learning scheme and kinda like the (cond
((assertion) (value))...(else (value))) syntax 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)))


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!
How about chaining ternary operators?


3 < 2 ? puts("3 is less than 2") :
3 > 3 ? puts("3 is greater than 3") :
3+1 > 4 ? puts("4 is greater than 4") :
puts("hey, it works!")
 
J

Jörg W Mittag

timr said:
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 <test> then <expression>
when <test>
<expression>
<expression>
else
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
 
T

timr

[Note:  parts of this message were removed to make it a legal post.]









Hi Rubyists,
I love ruby, but am learning scheme and kinda like the (cond
((assertion) (value))...(else (value))) syntax 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)))
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!

How about chaining ternary operators?

3   < 2 ? puts("3 is less than 2")      :
3   > 3 ? puts("3 is greater than 3")   :
3+1 > 4 ? puts("4 is greater than 4")   :
          puts("hey, it works!")

Woa, that is slick!
 
T

timr

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 <test> then <expression>
    when <test>
      <expression>
      <expression>
    else
      <expression>
    end












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















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 than3' },
              ->{ 3+1 > 4 } => ->{ '4 is greater than 4' },
                         else: ->{ 'hey, it works!' }


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.


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
 
T

timr

[Note:  parts of this message were removed to make it a legal post.]

Good Morning,

On Nov 11, 5:51 pm, Clifford Heath <[email protected]> wrote:
Case interrogates a single variable for its state--whether it falls in
a certain range, or is equal to such and such a value. The cond
statement is much more flexible, in that each assertion can be
completely unrelated to the other assertions--i.e. they can cover more
than the state of a single variable.

Nope, well sort of nope - you are right that it checks against a single
variable but you are missing the trick. However the default "variable" for
case is true and there are lots of options outside of the default as well..

As an example

x = 1
y = 2
case
  when x == 2 then puts 'x'
  when y == 2 then puts 'y'
end

That outputs a nice shiny "y"

or

x = 1
y = 2
case false
  when x == 2 then puts 'x'
  when y == 2 then puts 'y'
end

This outputs an even shiner "x"

And even nicer

x = 1
y = 2
case 2
  when x then puts 'x'
  when y then puts 'y'
end

A nice shiny 'y' pops up on the screen

John

Oh, I see. I have always seen it in the simplest form only:
print "Enter your grade: "
grade = gets.chomp
case grade
when "A"
puts 'Well done!'
when "B"
puts 'Try harder!'
when "C"
puts 'You need help!!!'
else
puts "You just making it up!"
end

Thanks. That is certainly better.
Tim
 
T

timr

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 <test> then <expression>
    when <test>
      <expression>
      <expression>
    else
      <expression>
    end












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















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 than3' },
              ->{ 3+1 > 4 } => ->{ '4 is greater than 4' },
                         else: ->{ 'hey, it works!' }


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.


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:?> #key => value rather than scheme's (cond (assertion)(value)...
(else(value)))**
?> tests.each_pair do |k,v|
?> if k.call?> cond l{3 < 2} => l{ puts "3 is greater 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!
=> nil

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top