CSC/ECE 517 Spring 2015/ch1a 9 RA: Difference between revisions
No edit summary |
|||
Line 80: | Line 80: | ||
end | end | ||
<pre | </pre> | ||
If you deal with large data and start chaining multiple enumeration methods together, the use of lazy evaluation prevents you from using unnecessary amounts of memory in temporary variables which makes lazy evaluation faster than non lazy evaluation. | If you deal with large data and start chaining multiple enumeration methods together, the use of lazy evaluation prevents you from using unnecessary amounts of memory in temporary variables which makes lazy evaluation faster than non lazy evaluation. | ||
Line 99: | Line 99: | ||
end | end | ||
<pre | </pre> | ||
Lazy enumeration is act as a proxy in between program and data so sometimes lazy enumeration gives non negligible performance hit. It is recommended not to use lazy enumeration everywhere as it adds another layer. | Lazy enumeration is act as a proxy in between program and data so sometimes lazy enumeration gives non negligible performance hit. It is recommended not to use lazy enumeration everywhere as it adds another layer. |
Revision as of 19:16, 1 February 2015
Lazy Enumerators
Introduction
In Ruby 1.8 and Ruby 1.9 the problem with enumeration that generate infinite sequences is that we have to write special, non-greedy, versions of methods. But, if you’re using Ruby 2.0 or later, you have this support built in. If you call Enumerator#lazy on any Ruby enumerator, you get back an instance of class Enumerator::Lazy. Many basic class like Array and Hash has lazy method through Enumerator class.
The lazy methods returns a lazy enumerator whose methods Map/collect, flat_map/ collect_caoncat, select/ find_all, reject, grep, zip, take, #take_while, drop, #drop_while, and cycle enumerate values only on as-needed basis.
Differences
If you try to iterate over an infinite range in Enumerator then it will run into an endless loop while with Enumerator::Lazy you can avoid endless loop and get just the value you need.
> range = 1..Float::INFINITY > range.collect { |n| n**2}.first(10) #endless loop
but with lazy,
> range = 1..Float::INFINITY > range.lazy.collect { |n| n**2}.first(10) => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Enumerator::Lazy acts just like the original, but it re-implements methods such as select and map so that they can work with infinite sequences. The lazy versions of the various methods do not return arrays of data. Instead, it returns a new enumerator that includes its own special processing—below example of map method returns an enumerator.
> range = 1..100 > range.map{|n| n**2}.take(10) #returns array => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
but with lazy,
> range.lazy.map{|n| n**2}.take(10) #returns enumerator =>#<Enumerator::Lazy:#<Enumerator::Lazy:#<Enumerator::Lazy:1..100>:map>:take(10)>
Examples
1.)
require 'date' require ‘benchmark’ Benchmark.bm(8) do |x| x.report("Non Lazy :") do puts (Date.new(2015)..Date.new(9999)).select{|d| d.day == 13 and d.friday?}.first(10) end x.report("Lazy :") do puts (Date.new(2015)..Date.new(9999)).lazy.select{|d| d.day == 13 and d.friday?}.first(10) end end
2). This example give you idea of using lazy enumeration in file read example and comparing with non lazy enumeration way
require 'benchmark' fileName = "C:\\wake-links.txt" Benchmark.bm(8) do |x| x.report("Non Lazy :") do 50.times { File.readlines(fileName).detect { |line| line =~ /110216/i} } end x.report("Lazy :") do 50.times { File.open(fileName).lazy.detect { |line| line =~ /110216/i} } end end
If you deal with large data and start chaining multiple enumeration methods together, the use of lazy evaluation prevents you from using unnecessary amounts of memory in temporary variables which makes lazy evaluation faster than non lazy evaluation.
3). We have seen usage and benefits of lazy enumeration in above examples but below simple example will give you drawback of it.
require 'benchmark' Benchmark.bm(8) do |x| x.report("Non Lazy :") do (1..10000000).select { |i| i % 3 == 0 || i % 5 == 0}.reduce(:+) end x.report("Lazy :") do (1..10000000).lazy.select { |i| i % 3 == 0 || i % 5 == 0}.reduce(:+) end end
Lazy enumeration is act as a proxy in between program and data so sometimes lazy enumeration gives non negligible performance hit. It is recommended not to use lazy enumeration everywhere as it adds another layer.