Using a block to surround a string?

Hi,

I’m trying something very simple, like passing a method a string, and
two more strings to surround that with… i.e.:

around_string(‘mystring’) {|b, a| b = ‘before’; a = ‘after’;}

The method:

def around_string(string, &block)
b, a = yield
“#{b}#{string}#{a}”
end

That of course doesn’t work because I still don’t get how blocks and
procs work and what they are. The reason I don’t just pass ‘a’ and ‘b’
as parameters to the method is because the method has other parameters I
don’t want to touch, and it seemed like a block as the last parameter
would be a good choice (I simplified my example so it’s clearer).

So I wanted to know what’s the best way to accomplish what I’m trying to
do…

Thanks for your patience :slight_smile:

  • Ivan V.

Read in the pickaxe about passing a hash of parameters to a method
(like Rails does a lot)

Aur

On 7/3/07, Ivan V. [email protected] wrote:

b, a = yield
"#{b}#{string}#{a}"

end

That of course doesn’t work because I still don’t get how blocks and
procs work and what they are. The reason I don’t just pass ‘a’ and ‘b’
as parameters to the method is because the method has other parameters I
don’t want to touch, and it seemed like a block as the last parameter
would be a good choice (I simplified my example so it’s clearer).

SonOfLilit makes the best suggestion, you can just use a hash as your
last parameter

def around_string(string,other,args,options={})

whatever

“#{options[:left]}#{string}#{options[:right]}”
end

around_string “foo”, …, :left => “$”, :right => “!”

But here’s some background on blocks.

def foo
a = yield # a gets the return value of the block
a + 1
end

foo { 4 } #=> 5

def foo
a = yield(3,5) # a passes 3 and 5 to the block
a + 1
end

foo { |b,c| b * c } #=> 16

So you see, you need to pass values to yield for them to be passed
into the block, and the result of the yield is just whatever the block
evaluates to.

Because it’s a closure you can also use things in the local scope.

d = 5
foo { |b,c| b * c + d } #=> 21

So if you really wanted to use blocks in your original example, you
could do:

def around_string(string, &block)
b, a = yield
“#{b}#{string}#{a}”
end

around_string(‘mystring’) { [‘before’,‘after’] }

But that is ugly and unidiomatic. :slight_smile:

Hope this helps,
-greg

But that is ugly and unidiomatic. :slight_smile:

Is the &block parameter necessary? When is it necessary?

And so I take it with blocks, they are given a new reference to the
objects
in question and therefore assigning to the variables in the block has no
effect on the method. ie

def around_string(string, &block)
b = nil
a = nil
yield(b,a)
“#{b}#{string}#{a}”
end

around_string(‘center’) { |b, a| b = ‘left’; a = ‘right’ }

is worthless because in the block I’m merely assigning objects to local
references that go out of scope once the block exits?

Matt

Hi –

On Wed, 4 Jul 2007, Ivan V. wrote:

b, a = yield
do…

Thanks for your patience :slight_smile:

The way a code block works, basically, is that it hangs off the right
of the method call (as you’ve got it); it contains code; and the
method has the ability to execute that code, using yield. The value
returned from the block, using standard Ruby statement/expression
evaluation, is the value of the yield statement itself.

Thus:

def my_method
x = yield
puts “I got back #{x}”
end

my_method { 10 } # I got back 10
my_method { “a string” } # I got back a string

In your case, you would want:

def around_string(string)
b,a = yield
“#{b}#{string}#{a}”
end

around_string(“abc”) { [“before”, “after”] } # beforeabcafter

Note that all the block has to do is provide a value. You can pass
arguments to a block – but in this case there’s not much point, since
all that the method actually needs is two strings (which I’ve packed
up in an array).

Also, note that you don’t need &block. &block is a special parameter
syntax that takes the code block and turns it into a Proc object. You
need that if you’re planning to pass the block around as an object, or
call another method using the same block; but if all you need to do is
yield, you can just do it.

David

P.S. You could also write your method as:

def around_string(string)
yield.join(string)
end

but that’s just for fun :slight_smile:

Hi –

On Wed, 4 Jul 2007, Matt G. wrote:

But that is ugly and unidiomatic. :slight_smile:

Is the &block parameter necessary? When is it necessary?

Only if you need to capture the block in an object.

around_string(‘center’) { |b, a| b = ‘left’; a = ‘right’ }

is worthless because in the block I’m merely assigning objects to local
references that go out of scope once the block exits?

Method definitions always have their own local scope, so a, b, and
string are strictly local to the method. Blocks pick up the scope
that they’re created in, and can also create variables that weren’t
already in that scope. Those variables disappear when the block
exits; the ones that were there already survive:

x = 1
some_method { y = x + 1 } # same x; new y
y # undefined; block’s y didn’t survive
x # same x

David

On 7/3/07, Matt G. [email protected] wrote:

Is the &block parameter necessary? When is it necessary?

You should not use &block and yield together. Use one or the other.

&block is necessary when you are passing a block to another method.

e.g.

def foo(&a)
puts “In foo”
bar(&a)
end

def bar
puts “In bar”
yield
end

foo { puts “in block” }

OUTPUT:

In foo
In bar
in block

And so I take it with blocks, they are given a new reference to the objects
in question and therefore assigning to the variables in the block has no
effect on the method. ie

def around_string(string, &block)
b = nil
a = nil
yield(b,a)
“#{b}#{string}#{a}”
end

around_string(‘center’) { |b, a| b = ‘left’; a = ‘right’ }

is worthless because in the block I’m merely assigning objects to local
references that go out of scope once the block exits?

Right, block local variables that are defined within the block
disappear when the block exits. Blocks can access and modify local
variables in the scope the block is defined, but not within the
method.

Here’s a set of examples all rolled together for that:

def foo
a = 1
yield(a)
puts “a in method #{a}”
end

b = 3

puts “b before block: #{b}”

foo { |a| b += 2; a += 1; puts “a in block: #{a}” }

OUTPUTS:

b before block: 3
a in block: 2
a in method 1
b after block: 5
puts “b after block: #{b}”

Ivan V. wrote:

around_string(‘mystring’) {|b, a| b = ‘before’; a = ‘after’;}

You want:

around_string(‘mystring’) { [‘before’, ‘after’] }

def around_string(string, &block)
b, a = yield
“#{b}#{string}#{a}”
end

And you wnat:

def around_string(string)
b, a = yield
“#{b}#{string}#{a}”
end

For a quick intro to blocks, see the five minute talk I gave on them
last week:

http://www.cogentconsulting.com.au/screencasts/index.html

A block, however, seems like a terrible way of doing this. Have you
considered using an options hash?

around_string(‘mystring’, :before => ‘before’, :after => ‘after’)

def around_string(string, *options)
“#{options[:before]}#{string}#{options[:after]}”
end

Pete Y.

small correction…

On 7/3/07, Gregory B. [email protected] wrote:

On 7/3/07, Matt G. [email protected] wrote:

puts “b before block: #{b}”

foo { |a| b += 2; a += 1; puts “a in block: #{a}” }

puts “b after block: #{b}”

def around_string(string)
yield.join(string)
end

puts around_string(“abc”) { [“before”, “after”] }

In message
[email protected], “Matt
Greer” writes:

And so I take it with blocks, they are given a new reference to the objects
in question and therefore assigning to the variables in the block has no
effect on the method. ie

Right.

references that go out of scope once the block exits?
Exactly. The block’s return in this case is just the string ‘right’
(the last
expression in the block).

You could easily do

a, b = yield

and call with

{ ‘left’, ‘right’ }

-s

Everyone wrote:

Very useful replies!

I’ve learned plenty from all your responses, and based on your input I’m
changing the way my code works.

Thanks a lot!

  • Ivan V.