CSC/ECE 517 Fall 2012/ch1b 1w60 ac: Difference between revisions
(Created page with "=SaaS - 3.8 yield()= ==Introduction==") |
m (→Quantifier) |
||
(32 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
=SaaS - 3.8 yield()= | =SaaS - 3.8 yield()= | ||
==Introduction== | ==Introduction (Preface)== | ||
In order to properly understand and cover the idea of how the Ruby <code>yield</code> function works, it is necessary to understand a few concepts that do not exist in other [http://en.wikipedia.org/wiki/Object-oriented_programming object oriented languages]. | |||
===Code Blocks & Closures=== | |||
Unlike Java (and several other object oriented languages) you can not only pass values and references, but you can also pass [http://en.wikipedia.org/wiki/Code_block blocks] of code as parameters in Ruby. A block of code that is both passable and can maintain all variables in it’s original scope, is called a closure. Here is an example of using a code block: | |||
<pre> | |||
codeblock = {puts "foo"} | |||
codeblock.call | |||
</pre> | |||
Executing this code will print: | |||
<pre> | |||
foo | |||
</pre> | |||
This is a very simple example, but it shows how code blocks can be defined, stored into a variable and called later. This is very important to understanding what the <code>yield</code> function does. | |||
An unbound closure, meaning the body of a closure itself, is referred to as a code block. A code block gives a shortcut, without explicitly creating an object, to pass around a block of code to be executed. This enables a programmer to quickly pass a block of code that they might want to implement across several objects or at several points of another function. | |||
===The Map & Grep Functions=== | |||
A great example of code blocks being used is in the map and grep functions. The Map function allows you to apply a function to every element of a list or collection, independent of the collection's type. A new collection is created based on the execution of the block on every element. An example would be: | |||
<pre> | |||
# This code example subtracts 2 from every element of the list | |||
mylist = mylist.map{|x| x-2} | |||
</pre> | |||
The Grep function allows you to search a collection for all elements that match a given criteria. Then a new collection is returned containing only those elements that match. An example would be. | |||
<pre> | |||
# This code example returns all elements less than 5 | |||
arr = arr.grep(|x| x<5) | |||
</pre> | |||
The way in which these two functions work depends on being able to run a block of code across several objects without ever examining the objects themselves. In Ruby, rather than iterating through a series of elements and calling an operation on them, we can pass a code block as an argument to a function that will then perform that code block whenever the correct conditions are satisfied. | |||
==The yield() Function== | |||
===What Is It?=== | |||
Simply put, the yield function in Ruby passes control to a user-defined code block. As simple as that statement is, it can be quite confusing, so here is a quick example<ref name="tutorialspoint">http://www.tutorialspoint.com/ruby/ruby_blocks.htm - 2012</ref>: | |||
<pre> | |||
def test | |||
puts "You are in the method" | |||
yield | |||
puts "You are again back to the method" | |||
yield | |||
end | |||
test {puts "You are in the block"} | |||
</pre> | |||
Executing this code results in: | |||
<pre> | |||
You are in the method | |||
You are in the block | |||
You are again back to the method | |||
You are in the block | |||
</pre> | |||
First, take a look at the last line of code. You'll notice that the parameter to the <code>test</code> function is actually a code block. Next, look at where the text "You are in the block" appears: after the "You are in the method" text then again after the "You are again back to the method" text. Notice how this corresponds to where the <code>yield</code> statements are in the code? That's because the <code>yield</code> statement is calling the code block, then returning control to the <code>test</code> method. | |||
===Syntax=== | |||
As you may have guessed from the previous example, the <code>yield</code> function's syntax is quite simple. In it's most basic form, the yield function can be called with: | |||
<pre> | |||
yield | |||
</pre> | |||
In this form, the yield function will simply execute the code block passed to the function it's called from. You can also pass parameters to the yield function which passes those parameters to the code block, like so: | |||
<pre> | |||
yield parameter | |||
</pre> | |||
Passing parameters allows you to call the same code block, but with different input. For example<ref name="tutorialspoint"/>: | |||
<pre> | |||
def test | |||
yield 5 | |||
puts "You are in the method test" | |||
yield 100 | |||
end | |||
test {|i| puts "You are in the block #{i}"} | |||
</pre> | |||
Executing this code results in: | |||
<pre> | |||
You are in the block 5 | |||
You are in the method test | |||
You are in the block 100 | |||
</pre> | |||
See how the same code block results in different output yielded to the first and second times? That's because the values <code>5</code> and <code>10</code> are passed each time, respectively, and output by the code block. | |||
===Using yield for Iterating=== | |||
The SaaS/Coursera video<ref name="saas">http://www.youtube.com/watch?v=yMV7nOiTwXw - 2012</ref> focuses on explaining the <code>yield</code> function as it relates to iterating over collections and explains it like so: | |||
The idea of using a <code>yield</code> statement to execute a block of code on an object is opposite of the concept used in several other OO languages, such as Java. Yielding turns the concept of [http://en.wikipedia.org/wiki/Iterator iterators] inside out, so that rather than iterating through a list to perform actions, you can pass the action to the list to perform. The comparison is made to Java as follows: | |||
'''Java''': Give me each element of a collection - I want to do something to each one. | |||
<pre> | |||
// List defined above | |||
listIterator litr = l.listIterator(); | |||
while(litr.hasNext()) { | |||
Object element = litr.next(); | |||
element.foo(); | |||
} | |||
</pre> | |||
'''Ruby''': Here is some code - do this to each element of this collection. | |||
<pre> | |||
# List defined above | |||
list.each { |x| x.foo() } | |||
</pre> | |||
This idea of sending a function to an object, rather than sending object to function creates (what some believe to be) a much more seamless and efficient style of code. In the example of iterators, Ruby both uses much less code, and enables the programmer to be completely abstracted as to how the list is iterated through. This prevents you from needing to define several functions that precede and follow your operations, and allows you to not have to expose methods to a [http://en.wikipedia.org/wiki/Namespace#Namespaces_in_Programming_Languages namespace] when calling a block of code on them. | |||
===Examples=== | |||
====Tree Traversal==== | |||
<ref name="csc517exam">https://docs.google.com/viewer?url=http%3A%2F%2Fcourses.ncsu.edu%2Fcsc517%2Fcommon%2Fhomework%2Ftests%2Fa1.pdf - 2008</ref> | |||
<pre> | |||
class Tree | |||
attr_reader :value | |||
def initialize(value) | |||
@value = value | |||
@children = [] | |||
end | |||
def <<(value) | |||
subtree = Tree.new(value) | |||
@children << subtree | |||
return subtree | |||
end | |||
def each | |||
yield value | |||
@children.each do |child_node| | |||
child_node.each { |e| yield e } | |||
end | |||
end | |||
end | |||
t = Tree.new() | |||
... # code to populate the tree | |||
t.each {|x| puts x} | |||
</pre> | |||
In this example, the yield function is used to allow the tree owner to do whatever they want to each element of a tree. This example just prints the name of each node of the tree, but passing a more complex code block would let you do whatever you like with each child. | |||
In this example, we create a class that includes Enumerable, providing access to the <code>each</code> method, among others. | |||
====Random Sequence==== | |||
<ref name="saas_pastebin">http://pastebin.com/T3JhV7Bk - 2012</ref> | |||
<pre> | |||
class RandomSequence | |||
def initialize(limit,num) | |||
@limit,@num = limit,num | |||
end | |||
def each | |||
@num.times { yield (rand * @limit).floor } | |||
end | |||
end | |||
i = -1 | |||
RandomSequence.new(10,4).each do |num| | |||
i = num if i < num | |||
</pre> | |||
This example comes from the SaaS video<ref name="saas"/> on the Ruby <code>yield</code> statement. | |||
In this example, the yield statement is used to execute the user defined code block for each random number the class generates. Each time <code>each</code> yields to the code block, <code>i</code> will be updated if the parameter to <code>yield</code> is the smallest value thus far. This is a great example of how passing a parameter to the <code>yield</code> function allows for concise code. | |||
====Quantifier==== | |||
<ref name="lec5">https://docs.google.com/viewer?url=http%3A%2F%2Fcourses.ncsu.edu%2Fcsc517%2Fcommon%2Flectures%2Fnotes%2Fol5.pdf - 2010</ref> | |||
<pre> | |||
module Quantifier | |||
def any? | |||
each {|x| return true if yield x} | |||
false | |||
end | |||
def all? | |||
each {|x| return false if not yield x} | |||
true | |||
end | |||
end | |||
class Array | |||
include Quantifier | |||
end | |||
puts [1, 2, 3].any? {|x| x == 5} | |||
# false | |||
puts [4, 3, 4].all? {|x| x == 4} | |||
# false | |||
puts [1, 2, 3].any? {|x| x == 3} | |||
#true | |||
puts [4, 4, 4].all? {|x| x == 4} | |||
#true | |||
</pre> | |||
In this example, covered in the Lecture 5 slides, we combine our knowledge of how iterators work with <code>yield</code>, and create our own versions of these iterators. The module <code>Quantifier</code> is created, that allows a collection (such as <code>Array</code>), to test whether any or all of a set of properties are held true. By examining <code>any</code>, you can see that for every element in the collection, you the <code>yield</code> function is called and it return true if a single element returns true. The <code>all</code> method returns false, if any <code>yield</code> call returns false. This extension of each using <code>yield</code> demonstrates how extensible <code>yield</code> is for iterating across elements. | |||
==Conclusion== | |||
As has been demonstrated in the previous examples and explanations, <code>yield</code> allows programmers to pass instructions to objects in Ruby that can be called at any time as if they were parameters to a function. This ability for Ruby to treat any block of code as an object, rather than just instantiations of classes, demonstrates true object orientation. This, as well as many other unique elements of Ruby, create a terse and simple implementation of extensive functions. | |||
==See Also== | |||
*[http://www.youtube.com/watch?v=yMV7nOiTwXw 3.8- yield()-SaaS-Armando Fox and David Patterson] - 2012 | |||
*[http://stackoverflow.com/questions/3066703/blocks-and-yields-in-ruby Blocks and Yields in Ruby @ StackOverflow] - 2010 | |||
*[http://stackoverflow.com/questions/4321737/what-does-the-yield-keyword-do-in-ruby What does the "yield" keyword do in Ruby? @ StackOverflow] - 2010 | |||
*[http://www.tutorialspoint.com/ruby/ruby_blocks.htm Ruby Blocks @ tutorialspoint] - 2012 | |||
*[https://docs.google.com/viewer?url=http%3A%2F%2Fcourses.ncsu.edu%2Fcsc517%2Fcommon%2Flectures%2Fnotes%2Fol5.pdf Class Notes CSC517 - Lecture 5] - 2010 | |||
==References== | |||
<references/> |
Latest revision as of 03:28, 11 October 2012
SaaS - 3.8 yield()
Introduction (Preface)
In order to properly understand and cover the idea of how the Ruby yield
function works, it is necessary to understand a few concepts that do not exist in other object oriented languages.
Code Blocks & Closures
Unlike Java (and several other object oriented languages) you can not only pass values and references, but you can also pass blocks of code as parameters in Ruby. A block of code that is both passable and can maintain all variables in it’s original scope, is called a closure. Here is an example of using a code block:
codeblock = {puts "foo"} codeblock.call
Executing this code will print:
foo
This is a very simple example, but it shows how code blocks can be defined, stored into a variable and called later. This is very important to understanding what the yield
function does.
An unbound closure, meaning the body of a closure itself, is referred to as a code block. A code block gives a shortcut, without explicitly creating an object, to pass around a block of code to be executed. This enables a programmer to quickly pass a block of code that they might want to implement across several objects or at several points of another function.
The Map & Grep Functions
A great example of code blocks being used is in the map and grep functions. The Map function allows you to apply a function to every element of a list or collection, independent of the collection's type. A new collection is created based on the execution of the block on every element. An example would be:
# This code example subtracts 2 from every element of the list mylist = mylist.map{|x| x-2}
The Grep function allows you to search a collection for all elements that match a given criteria. Then a new collection is returned containing only those elements that match. An example would be.
# This code example returns all elements less than 5 arr = arr.grep(|x| x<5)
The way in which these two functions work depends on being able to run a block of code across several objects without ever examining the objects themselves. In Ruby, rather than iterating through a series of elements and calling an operation on them, we can pass a code block as an argument to a function that will then perform that code block whenever the correct conditions are satisfied.
The yield() Function
What Is It?
Simply put, the yield function in Ruby passes control to a user-defined code block. As simple as that statement is, it can be quite confusing, so here is a quick example<ref name="tutorialspoint">http://www.tutorialspoint.com/ruby/ruby_blocks.htm - 2012</ref>:
def test puts "You are in the method" yield puts "You are again back to the method" yield end test {puts "You are in the block"}
Executing this code results in:
You are in the method You are in the block You are again back to the method You are in the block
First, take a look at the last line of code. You'll notice that the parameter to the test
function is actually a code block. Next, look at where the text "You are in the block" appears: after the "You are in the method" text then again after the "You are again back to the method" text. Notice how this corresponds to where the yield
statements are in the code? That's because the yield
statement is calling the code block, then returning control to the test
method.
Syntax
As you may have guessed from the previous example, the yield
function's syntax is quite simple. In it's most basic form, the yield function can be called with:
yield
In this form, the yield function will simply execute the code block passed to the function it's called from. You can also pass parameters to the yield function which passes those parameters to the code block, like so:
yield parameter
Passing parameters allows you to call the same code block, but with different input. For example<ref name="tutorialspoint"/>:
def test yield 5 puts "You are in the method test" yield 100 end test {|i| puts "You are in the block #{i}"}
Executing this code results in:
You are in the block 5 You are in the method test You are in the block 100
See how the same code block results in different output yielded to the first and second times? That's because the values 5
and 10
are passed each time, respectively, and output by the code block.
Using yield for Iterating
The SaaS/Coursera video<ref name="saas">http://www.youtube.com/watch?v=yMV7nOiTwXw - 2012</ref> focuses on explaining the yield
function as it relates to iterating over collections and explains it like so:
The idea of using a yield
statement to execute a block of code on an object is opposite of the concept used in several other OO languages, such as Java. Yielding turns the concept of iterators inside out, so that rather than iterating through a list to perform actions, you can pass the action to the list to perform. The comparison is made to Java as follows:
Java: Give me each element of a collection - I want to do something to each one.
// List defined above listIterator litr = l.listIterator(); while(litr.hasNext()) { Object element = litr.next(); element.foo(); }
Ruby: Here is some code - do this to each element of this collection.
# List defined above list.each { |x| x.foo() }
This idea of sending a function to an object, rather than sending object to function creates (what some believe to be) a much more seamless and efficient style of code. In the example of iterators, Ruby both uses much less code, and enables the programmer to be completely abstracted as to how the list is iterated through. This prevents you from needing to define several functions that precede and follow your operations, and allows you to not have to expose methods to a namespace when calling a block of code on them.
Examples
Tree Traversal
<ref name="csc517exam">https://docs.google.com/viewer?url=http%3A%2F%2Fcourses.ncsu.edu%2Fcsc517%2Fcommon%2Fhomework%2Ftests%2Fa1.pdf - 2008</ref>
class Tree attr_reader :value def initialize(value) @value = value @children = [] end def <<(value) subtree = Tree.new(value) @children << subtree return subtree end def each yield value @children.each do |child_node| child_node.each { |e| yield e } end end end t = Tree.new() ... # code to populate the tree t.each {|x| puts x}
In this example, the yield function is used to allow the tree owner to do whatever they want to each element of a tree. This example just prints the name of each node of the tree, but passing a more complex code block would let you do whatever you like with each child.
In this example, we create a class that includes Enumerable, providing access to the each
method, among others.
Random Sequence
<ref name="saas_pastebin">http://pastebin.com/T3JhV7Bk - 2012</ref>
class RandomSequence def initialize(limit,num) @limit,@num = limit,num end def each @num.times { yield (rand * @limit).floor } end end i = -1 RandomSequence.new(10,4).each do |num| i = num if i < num
This example comes from the SaaS video<ref name="saas"/> on the Ruby yield
statement.
In this example, the yield statement is used to execute the user defined code block for each random number the class generates. Each time each
yields to the code block, i
will be updated if the parameter to yield
is the smallest value thus far. This is a great example of how passing a parameter to the yield
function allows for concise code.
Quantifier
<ref name="lec5">https://docs.google.com/viewer?url=http%3A%2F%2Fcourses.ncsu.edu%2Fcsc517%2Fcommon%2Flectures%2Fnotes%2Fol5.pdf - 2010</ref>
module Quantifier def any? each {|x| return true if yield x} false end def all? each {|x| return false if not yield x} true end end class Array include Quantifier end puts [1, 2, 3].any? {|x| x == 5} # false puts [4, 3, 4].all? {|x| x == 4} # false puts [1, 2, 3].any? {|x| x == 3} #true puts [4, 4, 4].all? {|x| x == 4} #true
In this example, covered in the Lecture 5 slides, we combine our knowledge of how iterators work with yield
, and create our own versions of these iterators. The module Quantifier
is created, that allows a collection (such as Array
), to test whether any or all of a set of properties are held true. By examining any
, you can see that for every element in the collection, you the yield
function is called and it return true if a single element returns true. The all
method returns false, if any yield
call returns false. This extension of each using yield
demonstrates how extensible yield
is for iterating across elements.
Conclusion
As has been demonstrated in the previous examples and explanations, yield
allows programmers to pass instructions to objects in Ruby that can be called at any time as if they were parameters to a function. This ability for Ruby to treat any block of code as an object, rather than just instantiations of classes, demonstrates true object orientation. This, as well as many other unique elements of Ruby, create a terse and simple implementation of extensive functions.
See Also
- 3.8- yield()-SaaS-Armando Fox and David Patterson - 2012
- Blocks and Yields in Ruby @ StackOverflow - 2010
- What does the "yield" keyword do in Ruby? @ StackOverflow - 2010
- Ruby Blocks @ tutorialspoint - 2012
- Class Notes CSC517 - Lecture 5 - 2010
References
<references/>