Method that has a hash, and a proc arguments not running - ruby

I am not sure why this method is not running.
The compile error reads:
sequence5.rb:1:in `sequence5': wrong number of arguments (2 for 1) (ArgumentError) from sequence5.rb:11:in `<main>'
But as you can see clearly, sequence5 method should take 2 arguments, one for args hash and the other for b proc.
The code I'm trying to run is here.
def sequence5(args, &b)
n, m, c = args[:n], args[:m], args[:c]
i = 0
while i < n
b.call(i*m+c)
i += 1
end
end
p = Proc.new {|x| puts x}
sequence5({:n=>3, :m=>4, :c=>0}, p)

sequence5({:n=>3, :m=>4, :c=>0}, &p)
p is not an argument, it's a block and Ruby has different syntax for passing a block

Related

Ruby - Return A Proc.Call As Variable?

I'm trying to create a Ruby method that:
1. accepts as input a proc which accepts two arguments and
2. returns as output a proc which takes one argument (the other argument being passed in with the first proc.)
Code below. Everything I've read about how Ruby handles functional scope indicates that I should be able to return a proc.call as a variable, and I don't understand why I'm getting an "unexpected return" error (reproduced below the code)
(I'm obviously a beginning Rubyist...my experience has mostly been in JavaScript, where the mechanics of doing this sort of thing are, I think, much more intuitive.)
--
def partial (proc1, val1)
return Proc.new {|val2| return proc1.call(val1,val2)}
end
adder = Proc.new {|a,b| a + b}
sum_five = partial(adder,5)
sum_five.call(10) # -- expecting 15
# unexpected return
# (repl):13:in `block in partial'
# (repl):20:in `<main>'
In Ruby, procs and lambdas treat return differently.
Procs are designed to work within other Ruby control structures, so in that context a return is from the control structure. Lambdas are much more like stand-alone methods, so return returns from the lambda. You get an error because the proc has no context to return from.
Try this:
def partial (proc1, val1)
return lambda {|val2| return proc1.call(val1,val2)}
end
adder = Proc.new {|a,b| a + b}
sum_five = partial(adder,5)
puts sum_five.call(10) # -- expecting 15
Shorter/cleaner ruby syntax:
def composer(proc1, val1)
-> (val2) { proc1.call(val1, val2) }
end
adder = -> (a,b) { a + b }
sum_five = composer(adder, 5)
sum_five.call(10)
Although I don't understand it's name, Ruby already has a method, Proc#curry, that does this:
adder = Proc.new {|a,b| a + b}
sum_five = adder.curry.call(5)
sum_five.call(10) # => 15
sum_five.call(2) # => 7
just watch out, because curry takes an optional argument, which is the number of arguments it should expect before calling the initial proc, so if you pass it the argument you want as the "default" it'll work weird:
adder = Proc.new {|a,b| a + b}
sum_five = adder.curry(5) # NOTE: This is incorrect
sum_five.call(1) # => returns a Proc
sum_five[1][2][3][4][5] # => 3 (the first 2 arguments got passed, the rest ignored)

An integer I pass as an argument becomes a method

I am trying to write a function. Here is the code.
def get_sum(a,b)
if a == b do
return a
end
else
total = 0
for num in a...b
total += num
end
return total
end
end
I get this error:
undefined method `b' for main:Object (NoMethodError)
Can anyone tell me why I get this?
No do for ifs
if a == b
return a
end
Note that you can do the same thing so
def get_sum(a, b)
return a if a == b
(a...b).inject(:+)
end
It is because you have a block do ... end after b. Neither the keyword if nor the syntax sugar form ... == ... accepts a block. The only possibility left to interpret your code syntactically is to interpret b as a method that takes this do ... end block, and that is how it is parsed.

Global vs local variables?

I am wondering why my code works in one instance but doesn't in another. Does it have something to do with local and global variables?
This works:
def factorial num
result = 1
while (num > 1)
result = result * num
num -= 1
end
puts result
end
This doesn't work:
result = 1
def factorial num
while (num > 1)
result = result.to_i * num
num -= 1
end
puts result
end
Everything inside of a method definition cannot see local variables from other places. That sounds weird, but here's two ways to fix it:
result = 1
number = 10
def factorial(num,result_value)
while (num > 1)
result_value = result_value.to_i * num
num -= 1
end
puts result_value
end
factorial(number, result)
That passes result as an argument. That's a great way of handling the method because it doesn't allow you to change the value of result from within the method. That might not seem like a big deal but "pure methods" like this become very valuable as the size the code increases.
This is the "dirty" or un-pure way of doing the same thing:
#result = 1
def factorial(num)
while (num > 1)
#result = #result.to_i * num
num -= 1
end
puts #result
end
Putting an # in front of a variable name allows its scope to expand to methods defined outside of its scope. This becomes a problem as the complexity of your code increases.
Random personal opinion: even though Ruby doesn't require you to put the parentheses next to a method definition, you always should. It makes the code a lot more explicit and easier to read. Follow your heart though ;)
You could experiment by prepending all results with a $ sign, making it global. Prepending with a # results in an instance variable, also interesting. Sidenote: puts prints and returns nil, so your method returns nil.
result = 1 # line 1
def factorial num
while (num > 1)
result = result.to_i * num
num -= 1
end
puts result
end
In this code, factorial doesn't know about result variable from the line 1.
When Ruby find result = result.to_i * num in your method it will first assign nil to the result. Then Ruby will try to run result.to_i * num. Since result is already nil, result.to_i is equal 0.
Here is another example:
def foo
a = a
puts "#{a.class}"
end
foo #NilClass
In the Doesn't Work version the result variable you've assigned to 1 isn't visible inside the factorial method.
Now there is a possibly unexpected behaviour in Ruby that if you try to assign a variable and you refer to the same variable on the right hand side of the assignment, if that variable doesn't have a value yet then it is treated as nil rather than raising an error. So the first time round the loop when you perform
result = result.to_i * num
it's equivalent to result = nil.to_i * num and nil.to_i is equal to 0 so this then sets up result to be 0 for subsequent iterations of the loop and as you're just multiplying the value of result stays on 0.

Ruby: Why is 'while (p || (p=exns.shift))' not the same as 'while p ||= exns.shift'?

What's wrong here? I don't see how the two fragments can behave differently, yet they do.
Full code, try with one or the other while:
class T
def initialize
#e=[4,2]
end
def shift
r=#e.shift
puts "[#{r.inspect}]"
r
end
end
exns=T.new
while (p || (p=exns.shift))
#while p ||= exns.shift
puts "p: #{p.inspect}"
p -= 1
p=nil if p<1
puts " #{p.inspect}"
puts "T" if p
end
The 'while (p || (p=exns.shift))' never seems to short-circuit for some reason. I'd like to know the reason.
Your problem is with the letter you chose to name your variable - p is not like other letters (say q), since it is also the name of the Kernel method p():
p
# => nil
q
# NameError: undefined local variable or method `q' for main:Object
for that reason, because you did not define a local variable names p before the while loop, it is actually declared only inside the scope of the loop.
To see how this matters, simply add the following line before the loop:
p = nil
Now, both options act the same.

In ruby, what does "&block" do? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What’s this &block in Ruby? And how does it get passes in a method here?
I dont Understand the &block part, what does it do?
here is an example:
def method_missing(method_name, *args, &block)
#messages << method_name
#object.send method_name, *args, &block
end
Blocks give you an opportunity to state a callback to pass on to a method.
The & is key here - like #pst mentioned, it "promotes" the block to a Proc and binds the Proc to the variable with the given name.
With &
def time(&block)
puts block
end
time
# => nil
time { foo }
# => #<Proc:0x00029bbc>
Without &
def time(block)
puts block
end
time { foo }
# => ArgumentError: wrong number of arguments (0 for 1)
# Because & isn't included, the method instead expected an arguement,
# but as a block isn't a arguement an error is returned.
Answering "And how would I pass it to another method?" comment by Brian:
Like this:
def compose init_value, n=2, &b
if n==0 then init_value else
b.call( compose init_value, n - 1, &b )
end
end
compose 2 do |n| n * n end
#=> 16
compose 2, 4 do |n| n * n end
#=> 65536
compose 2, 4 do |n| n * 0.5 end
#=> 0.125
This is a recursive method that recursively applies the same block to a number several times. Here, the block packaged into b argument gets called, but at the same time it is passed on recursively to compose method, while n argument is decremented by 1. In the same way, b could be passed to any method, like map, reduce, anything.
Whereas, should you not need to pass the block to another method, you could simply use yield:
def apply_block_to_1_2_3
return yield( 1 ), yield( 2 ), yield( 3 )
end
apply_block_to_1_2_3 { |n| n * n }
#=> [1, 4, 9]
May the force be with you.
It converts the block to a proc object that can be passed on to another method.
when you call a method with a block, there are 2 ways to use that block:
call yield inside method
convert it into a Proc object by prepending & to it
with second way you can pass it to another method.
so in your case it transforms the given block into a Proc and calling method_name with it.
think of it as you can pass a block just like any argument.

Resources