Lambdas and only 1 argument in case statements in Ruby - ruby

As I get it, you can use a proc/lambda inside, switch, for example:
is_even = ->(x) { x % 2 == 0 }
case number
when 0 then puts 'zero'
when is_even then puts 'even'
else puts 'odd'
end
As I understand it, and from the examples I see, is it that lambdas can be used in case statements ONLY IF they accept only 1 parameter, since you can't do a case statement with more than 1 argument (case a, b isn't possible, except if maybe these elements are enclosed into an array). So basically if you try to use a lambda with more than 1 parameter in a case statement the code is going to break. Correct me if I'm wrong.

It's because:
is_even = ->(x) { x % 2 == 0 }
is_even.lambda? #true
is "lambda" ( is_even = lambda(x) { x % 2 == 0 } is equivalent of above code)
There exist procs:
is_even = proc{|x| x % 2 == 0 } # Proc.new{|x| x % 2 == 0 }
is_even.lambda? #false
lambdas checks for number of arguments. procs on other hand doesn't check number of arguments.
ppp1 = Proc.new{|x| x % 2 == 0 } #=> #<Proc:0x507f300#(pry):152>
ppp1.call(2,3,4,5) #true
ppp2 = ->(x){ x % 2 == 0 } # => #<Proc:0x2c5ac40#(pry):158 (lambda)>
ppp2.(2,4) # ArgumentError: wrong number of arguments (2 for 1)
You can read more here.

Related

Having trouble with conditionals

I'm trying to define a method that returns "fizz" when the integer is divisible by 3, "buzz" when is divisible by 5 and "fuzzbuzz" when the number is divisible by both. I know 15 is divisible by both, however I don't know what I'm inputting wrong in my code. Thank you.
def fizzbuzz(int)
if int % 3 == 0
return "Fizz"
elsif int % 5 == 0
return "Buzz"
elsif int % 15 == 15
return "FizzBuzz"
else
end
end
So for conditionals, ruby evaluate the if, then the elsif, and finally the else in order. If anything condition evaluates to true, then the rest of the conditions are ignored.
def fizzbuzz(int)
if int % 3 == 0
return "Fizz"
elsif int % 5 == 0
return "Buzz"
elsif int % 15 == 15
return "FizzBuzz"
else
end
end
So let's look at a few examples based on your current code.
Say int = 3
We go to if int % 3 == 0, which is true, so the code return "Fizz" and nothing below that is evaluated.
Say int = 5,
int % 3 == 0 is false, we move onto the first elsif.
int % 5 == 0 is true, we return "Buzz"
Now let's look at 15,
int % 3 == 0 is true, so the code will return "Fizz" and ignores everything else, even if there are more conditions that will evaluate to be true after the if statement.
The order here is very important. You will want to check if int % 15 == 0 first, then move onto % 3 and % 5.

Fizzbuzz switch statement

Long question but I think it is odd. I was playing around with ruby switch statements. Created a little fizzbuzz function to practice.
Initially created the code like this
def fizzbuzz(start_num, end_num)
fizzbuzz = []
(start_num..end_num).each do |x|
if x % 3 == 0 && x % 5 != 0
fizzbuzz << "fizz"
elsif x % 5 == 0 && x % 3 != 0
fizzbuzz << "buzz"
elsif (x % 3 == 0 && x % 5 == 0)
fizzbuzz << "fizzbuzz"
else
fizzbuzz << x.to_s
end
end
fizzbuzz
end
Works as expected. Then wanted to play with a switch statement. So I tried:
def fizzbuzz(start_num, end_num)
fizzbuzz = []
(start_num..end_num).each do |x|
case x
when x % 3 == 0
fizzbuzz << "fizz"
when x % 5 == 0
fizzbuzz << "buzz"
when (x % 3 == 0 && x % 5 == 0)
fizzbuzz << "fizzbuzz"
else
fizzbuzz << x.to_s
end
end
fizzbuzz
end
This time the code only prints out the number converted to a string. Then mistakenly I tried to add && to the end of every when statement like so
def fizzbuzz(start_num, end_num)
fizzbuzz = []
(start_num..end_num).each do |x|
case x
when x % 3 == 0 &&
fizzbuzz << "fizz"
when x % 5 == 0 &&
fizzbuzz << "buzz"
when (x % 3 == 0 && x % 5 == 0) &&
fizzbuzz << "fizzbuzz"
else
fizzbuzz << x.to_s
end
end
fizzbuzz
end
Interestingly this prints out the correct result. It is probably a trivial answer, but does anyone know why this is the case? It seems rather odd to me.
The when statements are doing a logical &&.
This has the side effect of concatenating your output when the condition is true.
The question you're actually asking, based on your comment, is what's going on with the when statements not seeming to work. The problem is that you wrote case x, which is evaluating x on-the-spot and comparing it to the when expressions.
Instead, use a "naked case", e.g.,
case
when (x % 3) == 0
# etc
Note also that this could be wrapped up a bit tighter, e.g.,
def fizzbuzz(start_num, end_num)
(start_num..end_num).collect do |x|
case
when (x % 3) == 0
"fizz"
when (x % 5) == 0
"buzz"
when (x % 3 == 0 && x % 5 == 0)
"fizzbuzz"
else
x.to_s
end
end
end
For the last piece of code, let's see one when condition in detail:
when x % 3 == 0 &&
fizzbuzz << "fizz"
Despite the indentation, it's equivalent to:
when x % 3 == 0 && fizzbuzz << "fizz"
Remember that && is short-circuit. The && expression returns its first argument if it is false. Otherwise, its second argument is evaluated and returned as the result.
So if x % 3 == 0 is false, then fizzbuzz << "fizz" is not executed. If x % 3 == 0 is true, fizzbuzz << "fizz" is executed. Exactly what is expected.

Ruby: FizzBuzz not working as expected

I'm having trouble getting an IF statement to produce the results I think they should. I'm not sure why I cannot get the && ("and") conditional to work.
def fizzbuzz(n)
pool = []
(1..n).each do |x|
if x % 3 == 0
pool.push('Fizz')
elsif x % 5 == 0
pool.push('Buzz')
elsif x % 3 == 0 && x % 5 == 0
pool.push('FizzBuzz')
else
pool.push(x)
end
end
puts pool
end
fizzbuzz(10)
and they results
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
I'm not sure what I'm doing wrong here.
Try this instead:
def fizzbuzz(n)
pool = []
(1..n).each do |x|
if x % 3 == 0 && x % 5 == 0
pool.push('FizzBuzz')
elsif x % 5 == 0
pool.push('Buzz')
elsif x % 3 == 0
pool.push('Fizz')
else
pool.push(x)
end
end
puts pool
end
When you use if/elsif/elsif/else, it will execute only one of this conditions at time. If x % 3 == 0, then that's it, ruby will no longer enter any of those conditions, that's why fizzbuzz will never be printed.
The if/else if/else branching only executes one of the code blocks. If a condition is true, then the following block is executed and the program will skip to the end of the if/else statements.
Here is another working version, which is a bit cleaner, but as Tiago Farias said, you wont get the 'fizzbuzz' message printed in a range from [1..10], because you don't have a value which will have the rest 0 for both % 3 and % 5, the closest will be 15.
def fizzbuzz(n)
#pool = []
(1..n).each do |x|
send_no x
end
puts #pool
end
def send_no x
return #pool << 'fizzbuzz' if x % 3 == 0 && x % 5 == 0
return #pool << 'fizz' if x % 3 == 0
return #pool << 'buzz' if x % 5 == 0
#pool << x
end
fizzbuzz(10)

Defining algebra on Proc

I am trying to define conjunction Proc#* and disjunction Proc#+ on Proc class. When the receiver and the argument have different arity, then it should raise an error. Otherwise, it should return a proc object that is conjunction/disjunction, respectively. I implemented them as follows:
class Proc
def * other
raise ArgumentError, "Arity does not match" unless arity == other.arity
->(*args){call(*args) and other.call(*args)}
end
def + other
raise ArgumentError, "Arity does not match" unless arity == other.arity
->(*args){call(*args) or other.call(*args)}
end
end
This works well with simple procs:
p = ->x{x % 2 == 0} * ->x{x % 3 == 0}
p.call(2) # => false
p.call(6) # => true
but it raises an error when I further try to build on the result of these methods:
q = p * ->x{x % 5 == 0}
# => Error, Arity does not match
This is because the arity of ->x{x % 5 == 0} is 1, whereas the arity of p is -1 due to call(*args) in my implementation.
Is there any good way to make the methods Proc#* and Proc#+ work recursively?
If I remove the raise ... from the definition, then it will work, but then, it will return misleading error messages when procs with different arity are conjoined/disjoined. For example, suppose the raise ... part is deleted from the definition above, and I do:
p = ->x{x % 2 == 0} * ->x, y, z{x % 3 == 0}
then Proc#* will not raise an error, but will return a valid proc object. However, since part of it requires one argument and another part requires three arguments, there would be no way to pass arguments to p in a valid way.
p.call(2) # => error
will raise an ArgumentError, saying:
Wrong number of arguments
but the mistake actually happened when creating p that cannot be satisfied with any number of arguments, and the error message would be misleading. That is why I added the raise ... check. Is removing the raise ... only way to make it work?
I think you probably need to remove the raise because procs that take variable numbers of arguments can be validly called but have different arity values. For example
p = ->(a) {} * (a, *args) {}
p.call(1) # valid
p.call(1,2) # invalid
Maybe a solution would be to improve the error message within the returned lambda? Something like
lambda(*args) do
raise "wrong number of arguments for left lambda" if (arity < 0 && arity.abs - 1 <= args.size) || (arity > 0 && arity != args.size)
# etc (not sure those cases are exactly right)
call(*args) and other.call(*args)
end
It may be easier to use the parameters method, which will spell out the required, optional and splat parameters. Should make it easy to check if it will never be possible to call the resulting lambda and throw an error on creation in that case.
One possible way is to extend Proc:
class MyProc < Proc
attr_reader :my_arity
def initialize(my_arity = nil, &block)
#my_arity = my_arity || block.arity
super(&block)
end
def *(other)
other = MyProc.new(&other)
raise "Arity error" unless my_arity == other.my_arity
MyProc.new(my_arity, &->(*args){ call(*args) && other[*args] })
end
end
p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x, y){ x == y })[1,1] #=> true
p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x, y){ x != y })[1,1] #=> false
p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x){ x })[1,1] #=> exception

ruby inject with conditional in block?

doing the first Project Euler question: summing the multiples of 3 and 5 between 1 and 1000, I came up with this (pretty simple)
sum = 0
1.upto(999) { |i| sum += i if 0 == i%3 || 0 == i%5 }
sum
but I thought this would work but it doesn't, can someone show me what I'm doing wrong, or why it doesn't work?
1.upto(999).inject(0) { |sum, i| sum + i if 0 == i%3 || 0 == i%5 }
thanks!
inject passes the result of the block through to the next iteration as the first argument. Your block will return nil when your if statement is false, which then gets passed back in as sum.
To get the correct answer, the block should return the current sum when it's false:
1.upto(999).inject(0) { |sum, i| (0 == i%3 || 0 == i%5) ? sum + i : sum }
Complementary answer: if you're about to tackle Euler problems you should start to build your own extensions of reusable code. In this case, the first extension would be Enumerable#sum:
module Enumerable
def sum
inject(0, :+)
end
end
And now you can write a solution that separates the condition of the summatory (you can read it out loud and it makes sense, that's typical of functional/declarative style):
1.upto(999).select { |x| x % 3 == 0 || x % 5 == 0 }.sum
You could even push it one step further and create Fixnum#divisible_by? so you can write:
1.upto(999).select { |x| x.divisible_by?(3) || x.divisible_by?(5) }.sum
More: here it's not a problem, but later on strict implementations (those using arrays) will require too much memory. Try then with laziness:
require 'lazy'
1.upto(999).lazy.select { |x| x % 3 == 0 || x % 5 == 0 }.sum
1.upto(999).inject(0) { |sum, i| sum += i if 0 == i%3 || 0 == i%5; sum }
Would also work (note the +=).
Or, use & proc which addresses self.
(1..999).select{|x| x%3==0||x%5==0}.inject &:+
(1..999).to_a.keep_if{|d| d%3 == 0 || d%5 == 0}.reduce(:+) for completeness.

Resources