Lazy Evaluation in Ruby

How to do lazy evaluation of Ruby arguments

You might be interested in using a Proc...

func = Proc.new {|n| n -1 }

def get_score_value(value, proc)
if value.nil?
return proc.call(0)
end
end

p get_score_value(nil, func)

Your proc is like a normal method, it can still test for nil and things like that.

Or, it allows you to provide separate functions to handle those situations:

func1 = Proc.new {|n| n -1 }
func2 = Proc.new { "was nil" }

def check_for_nil(value, funcNil, funcNotNil)
if value.nil?
return funcNil.call()
else
return funcNotNil.call(value)
end
end

p check_for_nil(nil, func2, func1)
p check_for_nil(1, func2, func1)

Also note the potential use of the or keyword in cases when you simply want to convert it to an empty or default type of the input (i.e. use 0 for numbers, [] for arrays, etc.)

def get_score_value(value)
(value or 0).round(2)
end

lazy evaluation and rspec's let method

You're seeing invocations of the lazy comment method where there are none.

However, comment will be lazy evaluated in the before block

No, the before block you've show us has nothing to do with let(:comment).

Using blog_post.comment is unrelated to Rspec, or let. It's using a method, comment, defined on your BlogPost model.

Then the comment block will be evaluated again in the "adds the comment" block

Again, no, there is no invocation whatsoever of comment inside that block. The only thing that's even close is blog_post.comments, comments plural, not comment, and that is accessing a has_many relationship on your model.

The only way to invoke the comment defined by let(:comment) is with the word comment. Methods that have same names but belong to different objects aren't relevant.

What's wrong with my ruby lazy evaluation

It is important to understand what y += 1 really does. y += 1 is the equivalent to:

y = y + 1

That means you just told Ruby you want to assign y + 1 to the local variable y. Because Ruby prefers to read from local variables over calling methods, it uses the current value of the local variabel y and tries to add 1. But the local variable y is still nil at this moment, therefore this operation raises a undefined method '+' for nil:NilClass exception.

I guess you expected y += 1 to call the method y, add 1 and write result back to @y. To achieve this you have to change your code a bit to:

class A
attr_writer :y

def x
self.y += 1
end

def y
@y ||= 0
end
end

a = A.new
a.x
#=> 1
a.x
#=> 2

self.y ensures that you read the method y and do not create a local variable. Furthermore you need a setter method attr_writer to be able to call y =.

Why does your second example work out of the box? Because << isn't a shortcut that creates a local variable, therefore it receives the array from y. And << shifts a value into that array in place without the need to call a setter method like y =.

Interesting read in this context: What Ruby’s ||= (Double Pipe / Or Equals) Really Does

Is there a way to force non-lazy evaluation of && and || in Ruby?

Perhaps you're looking for the | and & operators which do not short-circuit.

E.g.:

irb(main):007:0> true | (puts "hello")
hello
=> true
irb(main):008:0>

Documentation here for TrueClass. The same methods exist for FalseClass.

Lazy-evaluation of a #{}-string in ruby

I don't think you can dodge the ugly there. The interpolation happens before the call to dputs unless you put it inside a block, which postpones it until dputs evaluates it. I don't know where dputs comes from, so I'm not sure what its semantics are, but my guess is the block would get you the lazy evaluation you want. Not pretty, but it does the job.

Lazy evaluation in Ruby 2

It's so lazy it's not even doing the work - probably because you're not actually using the results of the operation. Put a sleep() in there to confirm:

> Benchmark.bm do |rep|
rep.report('lazy') { num.times do ; arr.lazy.map { |x| sleep(5) }; end }
rep.report('notlazy') { 1.times do ; [0,1].map { |x| sleep(5) } ; end }
end

user system total real
lazy 0.010000 0.000000 0.010000 ( 0.007130)
notlazy 0.000000 0.000000 0.000000 ( 10.001788)

Ruby - Lazily Evaluated Hash

GFD - not sure how I missed this: http://promise.rubyforge.org/

Ruby Challenge - Method chaining and Lazy Evaluation

Trivial, isn't it?

class Foo < Array
def self.bar
other = new
other << 'bar'
other
end
def self.baz
other = new
other << 'baz'
other
end
def bar
other = clone
other.unshift 'bar'
other
end
def baz
other = clone
other.unshift 'baz'
other
end
end

The to_s criterion fails because 1.9 has changed the way Array#to_s works. Change to this for compatibility:

Foo.baz.bar.to_s.should == ['bar', 'baz'].to_s

I want cake.

BTW - metaprogramming here would cut down the code size and increase flexibility tremendously:

class Foo < Array
def self.method_missing(message, *args)
other = new
other << message.to_s
other
end
def method_missing(message, *args)
other = clone
other.unshift message.to_s
other
end
end

How to create a memory efficient Ruby Pipe class with lazy evaluation?

This seems to work:

#!/usr/bin/env ruby

require 'pp'

class Pipe
def initialize
@commands = []
end

def add(command, options = {})
@commands << [command, options]

self
end

def run
enum = nil

@commands.each do |command, options|
enum = method(command).call enum, options
end

enum.each {}

enum
end

def to_s
cmd_string = "Pipe.new"

@commands.each do |command, options|
opt_list = []

options.each do |key, value|
if value.is_a? String
opt_list << "#{key}: \"#{value}\""
else
opt_list << "#{key}: #{value}"
end
end

cmd_string << ".add(:#{command}, #{opt_list.join(", ")})"
end

cmd_string << ".run"
end

private

def cat(enum, options)
Enumerator.new do |yielder|
enum.map { |line| yielder << line } if enum

File.open(options[:input]) do |ios|
ios.each { |line| yielder << line }
end
end.lazy
end

def cut(enum, options)
Enumerator.new do |yielder|
enum.each do |line|
fields = line.chomp.split(%r{#{options[:delimiter]}})

yielder << fields[options[:field]]
end
end.lazy
end

def grep(enum, options)
Enumerator.new do |yielder|
enum.each do |line|
yielder << line if line.match(options[:pattern])
end
end.lazy
end

def save(enum, options)
Enumerator.new do |yielder|
File.open(options[:output], 'w') do |ios|
enum.each do |line|
ios.puts line
yielder << line
end
end
end.lazy
end
end

p = Pipe.new
p.add(:cat, input: "table.txt")
p.add(:cut, field: 2, delimiter: ',\s*')
p.add(:grep, pattern: "4")
p.add(:save, output: "result.txt")
p.run

puts p


Related Topics



Leave a reply



Submit