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
Best Way to Combine Fragment and Object Caching for Memcached and Rails
Carrierwave Crop Specific Version
In Ruby How to Generate a Long String of Repeated Text
How to Delete Specific Characters from a String in Ruby
Ruby: How to Make a Public Static Method
How to Split a String in Ruby and Get All Items Except the First One
How to Write Specs for Code That Depends on Environment Variables
Ruby: How to Count the Number of Times a String Appears in Another String
Capistrano & Bash: Ignore Command Exit Status
Ruby Koan 151 Raising Exceptions
How to Group This Array of Hashes
Vcrproxy: Record Phantomjs Ajax Calls with Vcr Inside Capybara
Ruby Concatenate Strings and Add Spaces
Using Question Mark Character in Rails/Activerecord Column Name
How to Get an Array with Column Names of a Table