"no block given" with closure?

When I execute this code, I get "no block given (LocalJumpError)":

class S
   def initialize(&b1)
     @b1 = b1
   end
   def each
     @b1.call
   end
end

s = S.new {[1,2,3].each {|i| yield i }}.each {|v| puts v}

Changing the implied block/yield combination to an explicit proc object works, as follows:

class S
   def initialize(&b1)
     @b1 = b1
   end
   def each(&b2)
     @b1.call(b2)
   end
end

s = S.new { |p| [1,2,3].each { |i| p.call(i) }}.each {|v| puts v}

This prints the expected 3 lines.

This is with ruby 1.8.2 (2004-09-10) [i686-linux]

It's quite possible that I don't understand how this is supposed to work :-). Any hints would be appreciated.

Thanks,
Bob Sidebotham

I'd say, that as first closure @b1 does not have any block when it is
instantiated, so I't won't know of the block given to each. Therefore "no
block given".

On the other hand in the second example you stuff the block into the first
closure, such that it knows about the block.

So the bottom line would be: a closure remembers the state at its
creation, but it does not know about the state when it is called.

I hope it is correct and understandable.

regards,

Brian Schröder

···

On Thu, 23 Sep 2004 17:09:43 +0000, Bob Sidebotham wrote:

When I execute this code, I get "no block given (LocalJumpError)":

class S
   def initialize(&b1)
     @b1 = b1
   end
   def each
     @b1.call
   end
end

s = S.new {[1,2,3].each {|i| yield i }}.each {|v| puts v}

Changing the implied block/yield combination to an explicit proc object
works, as follows:

class S
   def initialize(&b1)
     @b1 = b1
   end
   def each(&b2)
     @b1.call(b2)
   end
end

s = S.new { |p| [1,2,3].each { |i| p.call(i) }}.each {|v| puts v}

This prints the expected 3 lines.

This is with ruby 1.8.2 (2004-09-10) [i686-linux]

It's quite possible that I don't understand how this is supposed to work
:-). Any hints would be appreciated.

Thanks,
Bob Sidebotham

--
Brian Schröder
http://www.brian-schroeder.de/

Bob Sidebotham wrote:

When I execute this code, I get "no block given (LocalJumpError)":

class S
  def initialize(&b1)
    @b1 = b1
  end
  def each
    @b1.call
  end
end

s = S.new {[1,2,3].each {|i| yield i }}.each {|v| puts v}

The yield looks for blocks only in the method where it is used.

I think this is by design.

You might be able to work around this by using eval with a binding. But that wouldn't be any better than the solution where you yield the block.

Regards,
Florian Gross

"Bob Sidebotham" <bob@windsong.bc.ca> schrieb im Newsbeitrag news:rrD4d.483473$M95.426973@pd7tw1no...

When I execute this code, I get "no block given (LocalJumpError)":

class S
  def initialize(&b1)
    @b1 = b1
  end
  def each
    @b1.call
  end
end

s = S.new {[1,2,3].each {|i| yield i }}.each {|v| puts v}

Changing the implied block/yield combination to an explicit proc object works, as follows:

class S
  def initialize(&b1)
    @b1 = b1
  end
  def each(&b2)
    @b1.call(b2)
  end
end

s = S.new { |p| [1,2,3].each { |i| p.call(i) }}.each {|v| puts v}

This prints the expected 3 lines.

This is with ruby 1.8.2 (2004-09-10) [i686-linux]

It's quite possible that I don't understand how this is supposed to work :-). Any hints would be appreciated.

Possibly. The yield in the first example has no block that it can invoke, much the same as when you invoke "yield" directly:

yield

LocalJumpError: no block given
        from (irb):1

You make it a bit too complicated, IMHO. If you just want to invoke some block on an Enumerable, then you can have that directly (the yield block in the first example basically just forwards the element). So you could do this as well:

class S
   def initialize(enum)
     @enum = enum
   end
   def each(&b)
     @enum.each &b
   end
end

s = S.new([1,2,3]).each {|v| puts v}

OTOH, if you want to use the block to generate a value which is used for the enumeration you could do

class S
   def initialize(&create)
     @create = create
   end
   def each(&b)
     @create.call.each &b
   end
end

s = S.new { [1,2,3] }.each {|v| puts v}

The subtle difference between these two is, that the second approach creates something on each invocation of S#each and iterates over it while in the first case all iterations run through the same instance:

s = S.new { [rand(10), rand(10)] }

=> #<S:0x100d43f0 @create=#<Proc:0x100d4450@(irb):41>>

s.each {|v| puts v}

1
6
=> [1, 6]

s.each {|v| puts v}

0
6
=> [0, 6]

?> s.each {|v| puts v}
0
5
=> [0, 5]

Hope this makes things a bit clearer.

Kind regards

    robert

Robert Klemme wrote:

You make it a bit too complicated, IMHO. If you just want to invoke some block on an Enumerable, then you can have that directly (the yield block in the first example basically just forwards the element). So you could do this as well:

Thanks Robert (and others) for your help. I had thought that yield somehow dynamically found the first block up the call stack and called that. Evidently, that's wrong, and yield will only call a block directly associated with the method.

I don't think I'm making this too complicated. The example I gave was distilled down from some stuff in Kansas. In Kansas, you can write:

kh.select(:Customer) { |c| c.cust_id > 100 }.each do |cust|
   puts "#{cust.cust_name}"
end

The nested blocks in my original example correspond to the above. In the current implementation of Kansas, the database query is executed all at once, and returns an array of records--that array is then passed back from the block, above, for iteration. I wanted this to be truly iterative so I wrapped up the sql in a class with an each method, as below:

class KSSelection
   def initialize(&doselect)
     @doselect = doselect
   end
   def each(&userblock)
     @doselect.call(userblock)
   end
end

def select(*args)
   tables,sql,read_only = check_query_args(*args)

   ...

   KSSelection.new do |userblock|
     @dbh.select_all(sql) do |row|
       userblock.call add_object(
        tables[0].new.load(row.to_h, self, read_only))
     end
   end
end

This code works (but of course, it didn't work when I naively used yield, instead of userblock.call, thinking that yield was dynamically scoped). If there's some way to do this without the stub KSSelection class, that'd be really neat, but I don't think there is.

Bob

P.S. Is there an actual Ruby language reference that would answer questions like this?

"Bob Sidebotham" <bob@windsong.bc.ca> schrieb im Newsbeitrag news:f5J4d.83622$%S.11627@pd7tw2no...

Robert Klemme wrote:

You make it a bit too complicated, IMHO. If you just want to invoke some block on an Enumerable, then you can have that directly (the yield block in the first example basically just forwards the element). So you could do this as well:

Thanks Robert (and others) for your help. I had thought that yield somehow dynamically found the first block up the call stack and called that. Evidently, that's wrong, and yield will only call a block directly associated with the method.

I don't think I'm making this too complicated. The example I gave was distilled down from some stuff in Kansas. In Kansas, you can write:

kh.select(:Customer) { |c| c.cust_id > 100 }.each do |cust|
  puts "#{cust.cust_name}"
end

The nested blocks in my original example correspond to the above. In the current implementation of Kansas, the database query is executed all at once, and returns an array of records--that array is then passed back from the block, above, for iteration. I wanted this to be truly iterative so I wrapped up the sql in a class with an each method, as below:

Ah, I see. Sounds reasonable especially for large results.

class KSSelection
  def initialize(&doselect)
    @doselect = doselect
  end
  def each(&userblock)
    @doselect.call(userblock)
  end
end

def select(*args)
  tables,sql,read_only = check_query_args(*args)

  ...

  KSSelection.new do |userblock|
    @dbh.select_all(sql) do |row|
      userblock.call add_object(
       tables[0].new.load(row.to_h, self, read_only))
    end
  end
end

This code works (but of course, it didn't work when I naively used yield, instead of userblock.call, thinking that yield was dynamically scoped). If there's some way to do this without the stub KSSelection class, that'd be really neat, but I don't think there is.

I dunno Kansas so this might not work or might not do what you want. But what about this simple passing on of the block?:

def select(*args, &b)
  ...
  @dbh.select_all(sql, &b)
end

Bob

P.S. Is there an actual Ruby language reference that would answer questions like this?

There's a pickaxe online version as well as a comprehensive lib documentation reachable via http://www.ruby-doc.org/

Regards

    robert