In the process of figuring out blocks and yields I came across these comma-separated assignments:
def fibit
n,m =1,1
loop do |variable|
yield n
n,m = m,n+m # this line
puts "n is #{n} m is #{m}"
end
end
fibit do |num|
puts "Next : #{num}"
break if num > 100
end
Why does the m get assigned first in this scenario?
Does the last one always go first? If so why?
This was also seen as only e has the value of 1 meaning e went first?
e,r=1
puts r
puts "-------"
puts e
Also, does anyone have an idea why the code-block versions just executes, where if I write the same code with no code block I actually need to call the method for it to run?
def fibit
n,m =1,1
loop do |variable|
puts "Next : #{n}"
break if n > 100
n,m = m,n+m
end
end
fibit
If I didn't have the last line it wouldn't run. Where in the first one I don't actually call the fibit method? Or is the block kicking it off?
m does not get assigned first. When using multiple assignments, all right hand side calculations are done before any assignment to the left hand side.
That's how this code works:
a = 1
b = 3
a, b = b, a
a
# => 3
b
# => 1
This would not be possible if the assignment was done serially, since you would get that both would be either equal 1 or 3.
To further prove my point, simply swap the assignment of n and m in your code, and you'll find that the result is the same:
def fibit
n,m =1,1
loop do |variable|
yield n
m,n = n+m,m # this line
puts "n is #{n} m is #{m}"
end
end
fibit do |num|
puts "Next : #{num}"
break if num > 100
end
Related
I am calling a method I created and trying to pass a range of values as the arguments.
My code:
def prime_numbers (x)
i = 1
count = 0
until i > x
if x % i == 0
count += 1
end
i += 1
end
if count > 2
puts "count is: " + count.to_s
p x.to_s + " is not prime."
p false
elsif count == 2
puts "count is: " + count.to_s
p x.to_s + " is prime."
p true
end
end
prime_numbers (5)
puts
prime_numbers (25)
puts
prime_numbers (31)
puts
prime_numbers (1..100) #This is the one I care about that is throwing an error
Desired output:
count is: 2
"5 is prime."
true
count is: 3
"25 is not prime."
false
count is: 2
"31 is prime."
true
This would be desired for all the numbers within the range.
What I think I've done incorrectly or may still need to do:
I could use an array and somehow incorporate blocks to do all of this.
I'm missing something very simple in my syntax
Syntax in my parameter list needs to be changed
Thank you in advance for your time answering this.
Your method is named prime_numbers, but that is not exactly what it does. It is hard to come up with a good name: it does too much. That said, you could call it for every number in a range like this :
(1..100).each{|n| prime_numbers(n) }
In order to pass an array, you should define a method in a way that it can process an array.
In your code from what I can see is you are getting a parameter x and then applying an operator > to that which in case of a range will throw an error.
When (1..100) is passed as an argument x becomes an array what you might wanna do is something like this
def prime_numbers (x)
y = x.class == Range ? x : [x] # to make sure you have an array
y.each do |number|
...... you code here
end
end
PS you would want to replace number in the above example. Or you could rename your parameter x
At ruby docs I found this piece of code:
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
Thing is i cant understand what it does. In general it calculates fibonacci's numbers, but I have hard time understanding the details. What is y (Enumerator::Yielder)? The docs says nothing about it. What does << method do? (I know its yield alias). Why does infinite loop happen when y << a
is removed?Thanks!
Consider the following code:
fib = Enumerator.new do |y|
puts "Enter enumerator"
a = b = 1
loop do
puts "Inside loop"
y << a
puts "y: #{y.inspect}, a: #{a}, b: #{b}"
a, b = b, a + b
end
end
puts fib.take(5)
It prints:
# Enter enumerator
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 1, b: 1
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 1, b: 2
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 2, b: 3
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 3, b: 5
# Inside loop
# 1
# 1
# 2
# 3
# 5
Apparently, this output actually gives hints on all the question you’ve stated. Note, that we entered a yielder only once. Let’s dig into:
Why loop is infinite?
Because Fibonacci’s number sequence is infinite. This enumerator is intended to be used with Enumerable#take (see an example above.)
What is Enumerator::Yielder?
It is an abstraction. It’s method yield actually calls back the block of callee, passing a parameter as block parameters.
What does << method does?
Yields once. In other words, it calls back the caller code, passing it’s parameter to the caller’s block. In this particular example, it will call back each block, passing a from Yielder instance as block parameter (e as I named it there.)
Why does infinite loop happen when y << a is removed?
Because there are no yields happened. In my example, the callee will stop after yielding five (5 as parameter of take) times.
In my teamtreehouse ruby course I am on this challenge and for some reason my solution is not clearing the challenge. What am I doing wrong
Question:
In the previous challenge, we wrote a method that returned the remainder of two arguments when divided. That's cool, but it would be nicer if the method returned the remainder in a nice full sentence. Use string interpolation to return the remainder inside the sentence “The remainder of a divided by b is c.” where a is your “a” variable, b is your “b” variable, and c is the value of a % b.
My Answer
def mod(a, b)
c = a % b
puts "The remainder of #{a} divided by #{b} is #{c}"
end
Note
We can only use two arguments
If you need to print and return your sentence:
def mod(a, b)
c = a % b
ret = "The remainder of #{a} divided by #{b} is #{c}"
puts ret
return ret
end
If you don't need to print out the sentence, you could do this:
def mod(a, b)
c = a % b
"The remainder of #{a} divided by #{b} is #{c}"
end
Ruby functions return the last value (if there's no earlier return statement) and puts returns nil always:
irb(main):003:0> puts "my sentence"
my sentence
=> nil
irb(main):004:0>
Modified previous answer and this worked for me :
def mod(a, b)
c = a % b
ret = "The remainder of #{a} divided by #{b} is #{c}."
return ret
end
puts mod(10, 3)
You forgot to add the period to the end of the sentence, that's why it didn't pass. Also change puts to return. I'm on the same challenge question and figured out my error.
The next statement is used to skip a part of the loop and continue with the next iteration of the loop. It can be used in combination with for and while statements.
I have seen people using next if there is complicated piece of code after some condition is being evaluated i.e
next if #state!=:some_state
# some long complicated code
Now here I have played with the next in my IRB as below :
n = 1
loop do
n = n + 1
next unless n == 10
print "Good"
break
end
# Good=> nil
The above one understood. Nicely clear.
n = 1
#=> 1
loop do
print "#{n}"
n = n + 1
next puts "hi" unless n == 5
p "good"
break
end
#1hi
#2hi
#3hi
#4"good"
#=> nil
In the above code, couldn't understand about which order the lines puts "hi" and unless n == 5 executed. Which executed first?
The below one leads to the infinite Loop.
n = 1
#=> 1
loop do
print "#{n}"
n = n + 1
next puts "hi"; 2 + 3 unless n == 5
p "good"
break
end
whereas this one is good:
n = 1
#=> 1
loop do
print "#{n}"
n = n + 1
next puts "hi", 2 + 3 unless n == 5
p "good"
break
end
#1hi
#5
#2hi
#5
#3hi
#5
#4"good"
#=> nil
Please help me here to understand - how does this one resolve that forever loop ?
A semicolon is evaled as a line break so:
next puts "hi"; 2 + 3 unless n == 5
would be equivalent to:
next puts "hi"
2 + 3 unless n == 5
Therefore, next will always be called and you'll have an infinite loop.
The comma is evaled as passing a set of arguments (which is interpreted as an array by puts method signature), so:
next puts "hi", 2 + 3 unless n == 5
is equivalent to:
next puts("hi", 2 + 3) unless n == 5
Regarding the execution order of puts and unless - consider the following:
unless n == 5
next puts "hi"
end
In this example, it is obvious that unless is evaluated first, then if the condition passed is evaluated to false the next puts "hi" statement is executed. Well:
next puts "hi" unless n == 5
is shorthand for exactly the same thing. So the unless modifier will always be evaluated first. Naturally, if you insert a semicolon in the middle, it would cause this to be evaluated differently, since the semicolon is evaluated as a line break.
The conditional has to execute first, otherwise, how would it even know whether to evaluate any expressions involving the statement?
As to your second question, the value of the next expression is ignored for loops, the value in the expression is only useful to return a value from a block. So, the only thing that makes your loop end is the break.
I'm on Chapter 33 of Learn Ruby the Hard Way.
Extra credit exercise 1 asks:
Convert this while loop to a function that you can call, and replace 6
in the test (i < 6) with a variable.
The code:
i = 0
numbers = []
while i < 6
puts "At the top i is #{i}"
numbers.push(i)
i = i + 1
puts "Numbers now: #{numbers}"
puts "At the bottom i is #{i}"
end
puts "The numbers: "
for num in numbers
puts num
end
My attempt:
i = 0
numbers = []
def loops
while i < 6
puts "At the top i is #{i}"
numbers.push(i)
i = i + 1
puts "Numbers now: #{numbers}"
puts "At the bottom i is #{i}"
end
end
loops
puts "The numbers: "
for num in numbers
puts num
end
As you can see, I got as far as trying to make the block into a function, not yet making the 6 a variable.
Error:
ex33.rb:5:in `loops': undefined local variable or method `i' for main:Object (Na
meError)
from ex33.rb:15:in `<main>'
from ex33.rb:15:in `<main>'
What am I doing wrong?
EDIT: Okay, improved it a little. Now the numbers variable is out of scope...
def loops (i, second_number)
numbers = []
while i < second_number
puts "At the top i is #{i}"
i = i + 1
numbers.push(i)
puts "Numbers now: #{numbers}"
puts "At the bottom i is #{i}"
end
end
loops(0,6)
puts "The numbers: "
for num in numbers
puts num
end
As #steenslag says, i is out of scope within loops. I would not recommend switching to using #i because i is only used by loops.
Your function is a utility that can be used to produce an array of numbers. The function uses i to figure out how far through it is (but the caller of the function doesn't care about this, it only wants the resulting numbers). The function also needs to return numbers, so move that inside loops too.
def loops
i = 0
numbers = []
while i < 6
puts "At the top i is #{i}"
numbers.push(i)
i = i + 1
puts "Numbers now: #{numbers}"
puts "At the bottom i is #{i}"
end
end
You now have to think about the fact that the caller of loops can no longer see numbers. Good luck with your learning.
When you say def, i goes out of scope. The method can't "see" it. Use #i in stead (the # gives the variable a greater "visibility"), or move the i=6 inside the method, or figure out how to use parameters with a method.
I may have misread the 'convert the while loop' but my solution was:
def loop(x, y)
i = 0
numbers = []
while i < y
puts "At the top i is #{i}"
numbers.push(i)
i += 1
puts "Numbers now: ", numbers
puts "At the bottom i is #{i}"
end
puts "The numbers: "
# remember you can write this 2 other ways?
numbers.each {|num| puts num }
end
loop(1, 6)