building blocks
One of the features I like best in Ruby is the support for blocks. Blocks are, essentially, chunks of code that can be passed around as objects and invoked at a later point in the program (also known as lambdas in other languages). But they are more than just code. When a block is defined, the context in which it is defined (variables, constants, etc) goes with it.
You define a block like this:
Either {} or do...end can be used as delimiters. The names between | are the arguments to the block. Any number of arguments can be specified by separating them with comma: |value, i, j|
To call the block:
The need for call may go away, though.
A little syntactic sugar allows us to pass blocks to methods implicitly. As far as I know, this syntax originated as a way to abstract looping behavior, one of the key applications of blocks in Ruby.
This is how it works:
Sweet, eh?
A method that takes a block can refer to it implicitly or explicitly. Implicitly:
The key here is the yield keyword, which calls the provided block with one or more values as arguments. yield can be called any number of times, for example:
But what if you need to keep a reference to the block, or pass the block around from within that method? Easy. If the last parameter in a method declaration is prefixed with &, the provided block will be assigned to the parameter. Take a look for yourself:
Going back to what I said about blocks taking the context they were defined in with them... it is true! I swear!
Ok, to illustrate the idea, look a this code:
Your first reaction might be "that won't work, a will not be available from within methodThatTakesBlock". In fact, it will, simply because a is part of the context where the block is defined.
You define a block like this:
b = lambda { |value| puts value }
c = lambda do |value|
puts value
end
c = lambda do |value|
puts value
end
Either {} or do...end can be used as delimiters. The names between | are the arguments to the block. Any number of arguments can be specified by separating them with comma: |value, i, j|
To call the block:
b.call("hello")
The need for call may go away, though.
A little syntactic sugar allows us to pass blocks to methods implicitly. As far as I know, this syntax originated as a way to abstract looping behavior, one of the key applications of blocks in Ruby.
This is how it works:
methodThatTakesBlock { |value| puts value }
Sweet, eh?
A method that takes a block can refer to it implicitly or explicitly. Implicitly:
def methodThatTakesBlock
yield "hello"
end
yield "hello"
end
The key here is the yield keyword, which calls the provided block with one or more values as arguments. yield can be called any number of times, for example:
def methodThatTakesBlock
yield "hello"
yield "world"
end
yield "hello"
yield "world"
end
But what if you need to keep a reference to the block, or pass the block around from within that method? Easy. If the last parameter in a method declaration is prefixed with &, the provided block will be assigned to the parameter. Take a look for yourself:
class BlockContainer
def initialize
@blocks = {}
end
def addBlock(name, &block)
@blocks[name] = block
end
end
container = BlockContainer.new
container.addBlock("a") { puts "a" }
container.addBlock("b") { puts "b" }
def initialize
@blocks = {}
end
def addBlock(name, &block)
@blocks[name] = block
end
end
container = BlockContainer.new
container.addBlock("a") { puts "a" }
container.addBlock("b") { puts "b" }
Going back to what I said about blocks taking the context they were defined in with them... it is true! I swear!
Ok, to illustrate the idea, look a this code:
def methodThatTakesBlock
yield
end
a = 1
methodThatTakesBlock { puts a }
yield
end
a = 1
methodThatTakesBlock { puts a }
Your first reaction might be "that won't work, a will not be available from within methodThatTakesBlock". In fact, it will, simply because a is part of the context where the block is defined.
Labels: ruby
0 Comments:
Post a Comment
<< Home