This question already has answers here:
Code block passed to each works with brackets but not with 'do'-'end' (ruby)
(3 answers)
Closed 8 years ago.
Saw a strange case come up, trying to figure out what is happening here:
> def test
> p yield
> end
=> nil
> test { 1 }
1
=> 1
> p test { 1 }
1
1
=> 1
> p test do
> 1
> end
LocalJumpError: no block given (yield)
The parser recognizes this
p test do
1
end
as this
p(test) do
1
end
The block is passed to p, not test. Therefore, yield can't yield and raises that error.
do and {} to denote blocks attached to methods are not completely interchangeable.
p test do
1
end
Precedence is screwing with you. This is actually this:
p(test()) do
1
end
So the block is getting passed to p, not test.
{} has higher precedence than do, and so binds more tightly to the syntactically closer method. This is also true for other ruby keywords that have symbolic equivalents, such as and/&& and or/||, which is why the symbols are usually recommended over the words.
Related
I'm practicing the While Loop in Ruby and got a basic example as below
i = 3
while i > 0 do
print i
i -= 1
end
My question is why I can't interchange do..end with {} as if I rewrite the above code as below it doesn't work anymore
i = 3
while i > 0 {
print i
i -= 1
}
However, it seems to work without the first "do"
i = 3
while i > 0
print i
i -= 1
end
Could anyone explain the rule or redirect me to the right resource? Thx!
As you said do is optional for while loop. While keyword is enough to define a block which is finished with end like any other block in ruby. In addition, end is mandatory for while block.
If you want to use while on just one line you can do such as below:
i = 0
i += 1 while i < 10
While mandatory needs end in Ruby.
Syntax example
do is optional and can be omitted.
So, it is not the case where pair do - end can be replaced with {}
This question already has answers here:
do..end vs curly braces for blocks in Ruby
(14 answers)
Closed 5 years ago.
I was trying to solve a coding puzzle: Take strings, and count the ones that don't include repeated words.
This code works:
def validate(passphrase)
words = passphrase.split
words == words.uniq
end
passphrases = File.readlines("../input/passphrases.txt")
p passphrases.count {|phrase| validate(phrase)}
#=> 337
If I make one minor change with the count block, it counts all of the passphrases instead of just the ones that would return true when passed through the block:
p passphrases.count do |phrase|
validate(phrase)
end
#=>512
What's up with this?
It is due to argument precedence with the do block.
Your first example is equivalent to:
p(passphrases.count {|phrase| validate(phrase)})
The second one with the do block is equivalent to:
p(passphrases.count) do |phrase|
validate(phrase)
end
Where the do is being applied to the p function.
If you want the second case to match the first, wrap the whole block in parenthesis:
p(passphrases.count do |phrase|
validate(phrase)
end)
I know that in ruby we can use a while loop, but I want to know if I can create a custom one so I can make something like this:
custom_while i < 5 do
puts i
i += 1
end
I currently have this code:
def custom_while(condition)
loop do
break if not condition
yield
end
end
i = 0
custom_while i < 5 do
puts i
i += 1
end
However, when condition is evaluated, it always get true (because it considers the first evaluation of i < 5 = true only.
Any help will be appreciated!
Note: This is for educational purposes only.
You almost had it. So, your problem is that the condition is only evaluated once? Well, what construct do we know that we can evaluate as often as we want? That's right: functions! So, let's make condition a function (or a Proc in Ruby lingo):
def custom_while(condition)
loop do
break unless condition.()
yield
end
end
i = 0
custom_while -> { i < 5 } do
puts i
i += 1
end
# 0
# 1
# 2
# 3
# 4
This is unfortunately not as nice looking as in other languages. Ruby's syntax and semantics are aggressively optimized for methods that take exactly 1 "function" as an argument. Ruby has a special syntactically and semantically light-weight construct for that, namely blocks. As soon as you have more than one, though, you're out of luck.
Compare this with languages that have proper block literals, like Smalltalk, for example. In Smalltalk, you could write a method while:do:, and call it like this:
i := 0.
while: [i < 5] do: [Transcript write: i. i := i + 1].
In fact, in Smalltalk, the syntax for blocks is so lightweight that there are no control structures at all in the language. if/then/else is simply an instance method of Boolean, for example:
i % 2 == 0 ifTrue: [Transcript write: "even"] ifFalse: [Transcript write: "odd"].
And while is actually an instance method of Block, so in reality, your example would look like this:
i := 0.
[i < 5] whileTrue: [Transcript write: i. i := i + 1]
Note: I make no guarantees for the Smalltalk code, I didn't test it.
The problem is, the condition is being evaluated before it's passed in, so it will never change.
Make the condition a function that you evaluate inside the loop, or use a macro to make it cleaner.
A method can invoke an associated block of code one or more times using the Ruby yield statement. we can also pass values to the block by giving parameters to yield in vertical bars (|). Just like I have done below.
1 def print_name
2 puts "Hello "
3 yield "Vikram"
4 end
5
6 animals = %w( ant bee cat dog elk )
7 animals.each {|animal| puts animal }
8 animals.each
9
10 print_name {|name| puts name}
11 print_name
In my code line number 11 is giving an error that :
`print_name': no block given (yield) (LocalJumpError)
This means we cannot use yield in a method without passing a block of code while calling the method.
My question is that, how in my above code "animals.each" (refer line 8) is working without giving any error, if there is a "yield" statement present inside "each" method of ruby?
If it is not present then
animals.each {|animal| puts animal }
this should not have worked.
Ruby allows you to check whether a block was passed to the current method by using Kernel#block_given?. Array#each, as the documentation says, returns an enumerator if no block is given (which is checked using block_given?).
Unlike each, your print_name method tries to yield regardless of whether a block was given, leading to the error on line 11.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I want to write a code that satisfies:
SomeClass.new.execute(method) == 3
and I have:
class SomeClass
def execute(method)
def method
yield
end
end
end
method = 1+2
which gives me nil. I'm still very confused about yield. Any help is greatly appreciated.
You're along the right lines, but your parameter method must be a code block.
You can create a code block in a few different ways. The most common is anonymously, using {...} or do...end. If you want to store the code block in a variable, which you'd need to do to call SomeClass.new.execute(method), you can use Proc.new.
There are other ways of creating blocks (using the lambda syntax), but they are beyond the scope of this question.
This will work using a block stored in a variable:
class SomeClass
def execute
yield
end
end
method = Proc.new { 1+2 }
SomeClass.new.execute(&method) # => 3
Or, more succinctly,
SomeClass.new.execute { 1 + 2 } # => 3
You're sort of in the right direction-ish. yield will execute a passed-in block. So you want to do something like this:
class SomeClass
def execute
yield
end
end
And then you can call it like this:
SomeClass.new.execute { 1+2 }
The yield keyword passes control to any block that was passed to the method. You haven't passed any blocks, so there's nothing to do.
You want something like this:
class C
def execute
yield # Pass control to a block that was passed in.
# Method returns whatever the value of evaluating `b` was.
end
end
Now you can do:
C.new.execute { 1 + 2 } # Pass block containing a statement "1 + 2".
# => 3 # The result of evaluating "1 + 2" is
# returned from the #execute method.
If you want to pass a method object corresponding to the block you want to pass, instead of specifying it anonymously, you can make a lambda:
b = lambda { 1 + 2 }
C.new.execute &b # Pass an object to #execute that will be used as
# => 3 # the block.
Ruby has a few mostly-equivalent ways of making lambdas:
lambda { 1 + 2 }
Proc.new { 1 + 2 }
-> { 1 + 2 }