Confusion with block local variable declaration with block variable declaration within the pipe `|`

Why are we not allowed to create local variables or new object with
block variables?

The below are allowed :

a = “hello world”.split(//).each{ |x;newstr = Array.new() | newstr =
x.capitalize;puts newstr }

H
E
L
L
O

W
O
R
L
D
=> [“h”, “e”, “l”, “l”, “o”, " ", “w”, “o”, “r”, “l”, “d”]

and

[1,2,3].each {|num|temp =0 ; temp = num ; print temp}
#123=> [1, 2, 3]

For what reasons the below are not allowed?

[1,2,3].each {|num;temp =0| temp = num;print temp}

#SyntaxError: (irb):5: syntax error, unexpected ‘=’, expecting ‘|’
#[1,2,3].each {|num;temp =0| temp = num;print}
^
#(irb):5: syntax error, unexpected ‘}’, expecting $end

from C:/Ruby193/bin/irb:12:in `’

and

a = “hello world”.split(//).each{ |x;newstr = Array.new() | newstr =
x.capitalize;puts newstr }

You’re looking for “Inject”

look for #each_with_object and #inject/#reduce

but i think in this sample is an job for #map/#collect

Joel P. wrote in post #1101045:

You’re looking for “Inject”
Module: Enumerable (Ruby 2.0.0)

No, please look at the symbol ;, which is used to declare block local
variables with the block parameters inside the |.

[1,2,3].each {}
=> [1, 2, 3]
[1,2,3].each {|x;y| print y}
=> [1, 2, 3]
[1,2,3].each {|x;y| y=x; print y}
123=> [1, 2, 3]

See above. Here x is block parameter and y is local variable to the
block. The only problem if you try to get that y to initialize in the
same place inside the |. - Why so?

NOTE: |x;y| and |x,y| has completely different meanings in Ruby

Hans M. wrote in post #1101051:

because this variables are reseted back to nil, every time the block is
run, and an “init” would not make sense

Yes! You are right. I know the importance of such declaration of local
variables inside the block.

Tell me the difference between |x,y| and |x;y=0| ? Hope answer to this
question will make sense to me.

and before you can ask: this parameters are NOT useless because they can
shadow other local variables from outside the block without overriding
them

because this variables are reseted back to nil, every time the block is
run, and an “init” would not make sense

in you want something that survive longer than one iteration look at
each_with_object / with_object or inject

and before you can ask: this parameters are NOT useless because they can
shadow other local variables from outside the block without overriding
them

its when each yields more than one object, then you can see a difference

{1=>“a”,2=>“b”,3=>“c”}.each{|x;y| p y}
nil
nil
nil

{1=>“a”,2=>“b”,3=>“c”}.each{|x,y| p y}
“a”
“b”
“c”

|x;y=0| is still an syntax error

Actually the error message is the clue here.

[1,2,3].each {|num;temp =0| temp = num;print temp}

#SyntaxError: (irb):5: syntax error, unexpected ‘=’, expecting ‘|’
#[1,2,3].each {|num;temp =0| temp = num;print}

Again why you assigning to temp and when you immediately overwrite it?
This
works

[1,2,3].each {|num;temp| temp = num;print temp}

Peter H. wrote in post #1101060:

Again why you assigning to temp and when you immediately overwrite it?

The below also doesn’t work.

[1,2,3].each {|num;temp =0| print temp}
SyntaxError: (irb):7: syntax error, unexpected ‘=’, expecting ‘|’
[1,2,3].each {|num;temp =0| print temp}
^
(irb):7: syntax error, unexpected tIDENTIFIER, expecting keyword_do or
‘{’ or ‘(

[1,2,3].each {|num;temp =0| print temp}
^
from C:/Ruby193/bin/irb:12:in `’

When we write the method block as below, its work.

def talk(x,y=1)
p “X = #{x}”
p “Y = #{y}”
end
=> nil

talk(5)
“X = 5”
“Y = 1”
=> “Y = 1”

talk(5,3)
“X = 5”
“Y = 3”
=> “Y = 3”

The same way I also tried to put the default value to temp as

[1,2,3].each {|num;temp =0| temp = num ; print temp}

it does not work because blocks does not allow “default” parameters like
that

blocks are different from methods.

But you can do that:

def talk(x,y=1)
p “X = #{x}”
p “Y = #{y}”
end

[1,2,3].each &method(:talk)
“X = 1”
“Y = 1”
“X = 2”
“Y = 1”
“X = 3”
“Y = 1”

Your first example does not work for me in any version of Ruby. But this
does

a = “hello world”.split(//).each{ |x,newstr = Array.new() | newstr =
x.capitalize;puts newstr }

Note the use of a , rather than a ; between the | … |
Also why are you assigning an Array to newstr? It makes absolutely no
sense
as x.capitalize will simply replace it with a string.

The use of , rather than ; holds for all your examples.

This has nothing to do with creating local variables and everything to
do
with writing bad code.

In the “Book of Ruby” author wrote the below paragraph:

a = “hello world”.split(//).each{ |x| newstr << x.capitalize } (<~~ But
this code is not working in my IRB. And I think it should not. As
newstr is not initialized before it’s used. - Is this the reason for
the code not to work.)

So, at each iteration, a capitalized character is appended to newstr,
and the following is displayed…

H
HE
HEL
HELL
HELLO
HELLO
HELLO W
HELLO WO
HELLO WOR
HELLO WORL
HELLO WORLD

As we are using the capitalize method here (with no terminating !
character), the characters in the array, a, remain as they began, all
lowercase, since the capitalize method does not alter the receiver
object (here the receiver objects are the characters passed into the
block). Be aware, however, that this code would not work if you were to
use the capitalize! method to modify the original characters. ** This is
because capitalize! returns nil when no changes are made so when the
space character is encountered nil would be returned and our attempt to
append (<<) a nil value to the string, newstr, would fail.**

I didn’t catch him with ***points.

" ".capitalize!
=> nil

a =" ".capitalize!
=> nil

a
=> nil

a= 12
=> 12

a =" ".capitalize!
=> nil

a
=> nil

No error I did encounter.

Can anyone tell me what’s the wrong? Does the author said wrong or my
caught is wrong?

capitalize! returns nil when no changes are made

so you can do:

if(str.capitalize!)
puts “Changed!”
else
puts “Not Changed!”
end

append (<<) a nil value to the string

a=""
a << " ".capitalize! #raise an error

Okay!

Thanks for your help!

a=""
=> “”

a << " ".capitalize!
TypeError: can’t convert nil into String
from (irb):2
from C:/Ruby193/bin/irb:12:in `’

Got the above one.

Well. a = "hello world".split(//).each{ |x| newstr << x.capitalize }
is running on your IRB. I think it should not.

a = “hello world”.split(//).each{ |x| newstr << x.capitalize }
NameError: undefined local variable or method newstr' for main:Object from (irb):3:inblock in irb_binding’
from (irb):3:in each' from (irb):3 from C:/Ruby193/bin/irb:12:in

The error is so obvious. But don’t know why author choose this code to
explain.:frowning:

maybe the newstr was defined in the paragraph before, or there is a
failure in the book

Thanks for your help!

a=""
=> “”

a << " ".capitalize!
TypeError: can’t convert nil into String
from (irb):2
from C:/Ruby193/bin/irb:12:in `’

Got the above one.

did you finally understand the difference between capitalize and
capitalize! and why

this works:
newstr =""
a = “hello world”.split(//).each{ |x| newstr << x.capitalize }

but this does not?
newstr =""
a = “hello world”.split(//).each{ |x| newstr << x.capitalize! }

??

Hans M. wrote in post #1101086:

did you finally understand the difference between capitalize and
capitalize! and why

this works:
newstr =""
a = “hello world”.split(//).each{ |x| newstr << x.capitalize }

but this does not?
newstr =""
a = “hello world”.split(//).each{ |x| newstr << x.capitalize! }

??

You explained it very cool way. So I have to digest it and I did also.

Thankkkkk youuuuu veryyyyy much :slight_smile: :):smiley:

Am 11.03.2013 13:22, schrieb Love U Ruby:

H

I didn’t catch him with ***points.
=> nil

a
=> nil

No error I did encounter.

Because you did not try to append nil to a string!