Difference between proc.call and yield?

Consider the following contrived examples:

def func1()
ret = []
t = nil
5.times do |i|
t = yield i
ret << t
end
ret
end

func1 { |b| b + 10 } # [ 10, 11, 12, 13, 14 ]

def func2(&block)
ret = []
t = nil
5.times do |i|
t = block.call(i)
ret << t
end
ret
end

func2 { |b| b + 10 } # [ 10, 11, 12, 13, 14 ]

Is this just TMTOWTDI, or is there a difference?

Dan

···


/^Dan Debertin$/
airboss@nodewarrior.org
www.nodewarrior.org
ignorami: n:
The art of folding problem users into representational shapes.

Consider the following contrived examples:

But I hate contrived examples!

def func1()
ret = []
t = nil
5.times do |i|
t = yield i
ret << t
end
ret
end

func1 { |b| b + 10 } # [ 10, 11, 12, 13, 14 ]

def func2(&block)
ret = []
t = nil
5.times do |i|
t = block.call(i)
ret << t
end
ret
end

func2 { |b| b + 10 } # [ 10, 11, 12, 13, 14 ]

Is this just TMTOWTDI, or is there a difference?

AFAIK, TMTOWTDI.

GS

···

----- Original Message -----
From: “Dan Debertin” airboss@nodewarrior.org

Dan

Dan Debertin airboss@nodewarrior.org writes:

  t = yield i
  t = block.call(i)

Is this just TMTOWTDI, or is there a difference?

There’s an internal difference, in that ‘yield’ does not create a proc
object, while (&block) does. This might make a performance
difference. Let’s find out…

 require "benchmark"
 include Benchmark

 N = 500000

 def block(&b)
   N.times { b.call(1) }
 end

 def no_block
   N.times { yield 1 }
 end

 bm(10) do |test|

   test.report("block")    { block {|i|}    }
   test.report("no block") { no_block {|i|} }
   test.report("block")    { block {|i|}    }
   test.report("no block") { no_block {|i|} }

 end

 dave[~/tmp 22:58:57] ruby t.rb
                 user     system      total        real
 block       3.430000   0.000000   3.430000 (  3.450126)
 no block    1.320000   0.000000   1.320000 (  1.383497)
 block       3.450000   0.000000   3.450000 (  3.450232)
 no block    1.330000   0.000000   1.330000 (  1.329939)

Yup - there’s a difference :slight_smile:

Cheers

Dave

The parameter passing is also different, quoting Matz (from ruby-talk
46413):

First, you have to learn several tips:

(a) block parameter |v| means takes everything in a single variable,
which is better described as |*v|.

(b) “call” requires exact number of required argument, whereas
"yield" fills nil for fewer arguments and ignores too much
arguments.

HTH,

– Shanko

“Dan Debertin” airboss@nodewarrior.org wrote in message
news:87ptwitcpq.wl@discus.nodewarrior.org

···

Consider the following contrived examples:

def func1()
ret = []
t = nil
5.times do |i|
t = yield i
ret << t
end
ret
end

func1 { |b| b + 10 } # [ 10, 11, 12, 13, 14 ]

def func2(&block)
ret = []
t = nil
5.times do |i|
t = block.call(i)
ret << t
end
ret
end

func2 { |b| b + 10 } # [ 10, 11, 12, 13, 14 ]

Is this just TMTOWTDI, or is there a difference?

Dan

/^Dan Debertin$/
airboss@nodewarrior.org
www.nodewarrior.org
ignorami: n:
The art of folding problem users into representational shapes.

“Dave Thomas” Dave@PragmaticProgrammer.com wrote in message
news:m2vg6anngv.fsf@zip.local.thomases.com

Dan Debertin airboss@nodewarrior.org writes:

  t = yield i
  t = block.call(i)

Is this just TMTOWTDI, or is there a difference?

There’s an internal difference, in that ‘yield’ does not create a proc
object, while (&block) does. This might make a performance
difference. Let’s find out…

In recursive situations it’s sort of advantageous to use the block
form since it prevents the creation of unnecessary blocks.
Furthermore, it’s more elegant …

···

$ cat stack.rb
class Stack
NullStack = new

def initialize()
@top = self
@rest = NullStack
end

def each_mixed(&b)
yield @data
@rest.each_mixed(&b)
end

def each_block(&b)
b.call(@data)
@rest.each_block(&b)
end

def each_yield
yield @data
@rest.each_yield {|r| yield r }
end

def push(data)
@data = data
@rest = clone
end

def pop()
unless empty?
data = @data
@rest = @rest.rest
data
else
nil
end
end

def empty?; NullStack.equal?(@rest) end
protected
attr_reader :rest

class << NullStack
protected
def each_mixed; end
def each_block; end
def each_yield; end
# undef all unapplicable methods
undef :pop
# etc.
end
end

stack = Stack.new
1000.times {|i| stack.push(i) }

require 'benchmark’
Benchmark::bmbm {|x|
x.report(“each_mixed”) { stack.each_mixed {|i| ii + 3iii } }
x.report(“each_block”) { stack.each_block {|i| ii + 3iii } }
x.report(“each_yield”) { stack.each_yield {|i| ii + 3iii } }
}

$ ruby stack.rb
Rehearsal ----------------------------------------------
each_mixed 0.170000 0.020000 0.190000 ( 0.184000)
each_block 0.080000 0.000000 0.080000 ( 0.089000)
each_yield 14.761000 0.010000 14.771000 ( 15.143000)
------------------------------------ total: 15.041000sec

             user     system      total        real

each_mixed 0.080000 0.000000 0.080000 ( 0.083000)
each_block 0.080000 0.000000 0.080000 ( 0.087000)
each_yield 12.929000 0.000000 12.929000 ( 13.101000)

/Christoph

"Christoph"wrote

def push(data)
@data = data
@rest = clone
end

Sigh, …

def push(data)
    @rest = clone
    @data = data
end

/Christoph