CSC/ECE 517 Spring 2015/ch1a 9 RA: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 53: Line 53:
require 'date'
require 'date'
require ‘benchmark’
require ‘benchmark’
Benchmark.bm(8) do |x|
Benchmark.bm(8) do |x|
x.report("Non Lazy :") do
x.report("Non Lazy :") do
Line 70: Line 69:


require 'benchmark'
require 'benchmark'
fileName = "C:\\wake-links.txt"
fileName = "C:\\sample.txt"
 
Benchmark.bm(8) do |x|
Benchmark.bm(8) do |x|
x.report("Non Lazy :") do
x.report("Non Lazy :") do
Line 90: Line 88:


require 'benchmark'
require 'benchmark'
Benchmark.bm(8) do |x|
Benchmark.bm(8) do |x|
x.report("Non Lazy :") do
x.report("Non Lazy :") do

Revision as of 02:52, 2 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:\\sample.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.