Forum: Ruby Confusion with block local variable declaration with block variable declaration within the pipe `|`

Posted by Love U Ruby (my-ruby)
on 2013-03-11 10:57
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 `<main>'

and

a = "hello world".split(//).each{ |x;newstr = Array.new() | newstr =
x.capitalize;puts newstr }
Posted by Joel Pearson (virtuoso)
on 2013-03-11 11:00
Posted by Hans Mackowiak (hanmac)
on 2013-03-11 11:04
look for #each_with_object and #inject/#reduce

but i think in this sample is an job for #map/#collect
Posted by Love U Ruby (my-ruby)
on 2013-03-11 11:05
Joel Pearson wrote in post #1101045:
> You're looking for "Inject"
> http://ruby-doc.org/core-2.0/Enumerable.html#method-i-inject

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
Posted by Hans Mackowiak (hanmac)
on 2013-03-11 11:12
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
Posted by Love U Ruby (my-ruby)
on 2013-03-11 11:17
Hans Mackowiak 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
Posted by Hans Mackowiak (hanmac)
on 2013-03-11 11:22
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
Posted by Peter Hickman (Guest)
on 2013-03-11 11:22
(Received via mailing list)
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.
Posted by Peter Hickman (Guest)
on 2013-03-11 11:29
(Received via mailing list)
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}
Posted by Love U Ruby (my-ruby)
on 2013-03-11 11:37
Peter Hickman 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 `<main>'

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}
Posted by Hans Mackowiak (hanmac)
on 2013-03-11 11:45
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"
Posted by Love U Ruby (my-ruby)
on 2013-03-11 13:22
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?
Posted by Hans Mackowiak (hanmac)
on 2013-03-11 13:27
***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
Posted by Love U Ruby (my-ruby)
on 2013-03-11 13:31
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 `<main>'

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:in `block in irb_binding'
        from (irb):3:in `each'
        from (irb):3
        from C:/Ruby193/bin/irb:12:in `<main>'

The error is so obvious. But don't know why author choose this code to 
explain.:(
Posted by Hans Mackowiak (hanmac)
on 2013-03-11 13:43
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 `<main>'

>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! }


??
Posted by Love U Ruby (my-ruby)
on 2013-03-11 13:47
Hans Mackowiak 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 :) :):D
Posted by unknown (Guest)
on 2013-03-11 21:00
(Received via mailing list)
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!
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.