Indexers and blocks?

Hi,

if I have an array-like class, is it possible to call the index with a
block?

class MyClass
  def find(key)
    return nil
  end

  def [](key)
    if (val = find(key))
      return val
    else
      return yield(key) if block_given?
    end
  end
end

c = MyClass.new
c[:test] { "default" }

triggers a syntax error (Ruby 1.8.6).

Is there a way to accomplish this or do i need to do tricks like giving
the block to the constructor?

Cheers,

Steph.

Hi --

···

On Tue, 9 Oct 2007, Stephan Mueller wrote:

Hi,

if I have an array-like class, is it possible to call the index with a
block?

class MyClass
def find(key)
   return nil
end

def (key)
   if (val = find(key))
     return val
   else
     return yield(key) if block_given?
   end
end
end

c = MyClass.new
c[:test] { "default" }

triggers a syntax error (Ruby 1.8.6).

Is there a way to accomplish this or do i need to do tricks like giving
the block to the constructor?

You can do:

   c.(:text) { "default" }

but that's kind of ugly. I guess the whole point of c is to conceal
the fact that it's a method call, which means that it then doesn't try
to duplicate the full method call anatomy.

David

--
Upcoming training from Ruby Power and Light, LLC:
   * Intro to Ruby on Rails, Edison, NJ, October 23-26
   * Advancing with Rails, Edison, NJ, November 6-9
Both taught by David A. Black.
See http://www.rubypal.com for more info!

I agree with you that this is unfortunate for the general case.
In your case however -- unless it was only an example -- I would favor the

following idiom

c[:test] or "default"

HTH
Robert

···

On 10/9/07, Stephan Mueller <d454d@web.de> wrote:

Hi,

if I have an array-like class, is it possible to call the index with a
block?

class MyClass
  def find(key)
    return nil
  end

  def (key)
    if (val = find(key))
      return val
    else
      return yield(key) if block_given?
    end
  end
end

c = MyClass.new
c[:test] { "default" }

triggers a syntax error (Ruby 1.8.6).

--
what do I think about Ruby?
http://ruby-smalltalk.blogspot.com/

Hi,

* David A. Black <dblack@rubypal.com> [09.10.2007]:

You can do:

  c.(:text) { "default" }

ah, thanks, have not thought of that.

but that's kind of ugly. I guess the whole point of c is to conceal
the fact that it's a method call, which means that it then doesn't try
to duplicate the full method call anatomy.

at least its better than having a # and a #get(..) member where one
forwards to the other one. Will this change in 1.9 or later?

Cheers,

Steph.

* Robert Dober <robert.dober@gmail.com> [09.10.2007]:

I agree with you that this is unfortunate for the general case.
In your case however -- unless it was only an example -- I would favor the

following idiom

c[:test] or "default"

nice! But in my case I will need the block because

a) in practice a simple literal will not suffice
b) the result of the block should be assigned to the internal array at
the same step. (this was not part of my example code, sorry)

Without these requirements I like your idea best... :slight_smile:

Cheers,

Steph.

* Robert Dober <robert.do...@gmail.com> [09.10.2007]:
> c[:test] or "default"

nice! But in my case I will need the block because

a) in practice a simple literal will not suffice

You're not limited to a simple literal.

b) the result of the block should be assigned to the internal array at
the same step. (this was not part of my example code, sorry)

Your example seems more hash-like than array-like to me, and it sounds
like using the same technique that Hash uses (passing a block to
Hash.new for default value computation) may work for you if the
computed value is a function of the object and/or the key.

class MyClass
  def initialize obj=nil, &b
    if block_given?
      @default_block = b
    elsif obj
      @default_block = lambda {|h,k| obj }
    end
    @h = Hash.new
  end

  def (key)
    if !@h.has_key?(key) && @default_block
      @h[key] = @default_block.call(self, key)
      puts "'#{@h[key]}' assigned to #{key}"
    end
    @h[key]
  end

  def =(key, value)
    @h[key] = value
  end
end

c = MyClass.new('default')
c[:foo] = 'foo'
puts c[:foo]
puts c[:test]
c = MyClass.new {|obj,key| "default for #{key}" }
puts c[:test]

···

On Oct 9, 10:40 am, Stephan Mueller <d4...@web.de> wrote:

This looks to me like you've just recreated features
of Hash. Might as well just use Hash.

I'd also like to point out Hash#fetch, which allows
you to override the hash default logic:

a = Hash.new(0)
a[1] # 0
a.fetch(1) { |k| -k} # -1
a[1] # 0
a.fetch(1) # IndexError exception

Gary Wright

···

On Oct 9, 2007, at 1:50 PM, Brian Adkins wrote:

c = MyClass.new('default')
c[:foo] = 'foo'
puts c[:foo]
puts c[:test]
c = MyClass.new {|obj,key| "default for #{key}" }
puts c[:test]

Presumably the OP had a reason for wanting an "array-like class"
instead of an actual Array or Hash. I only used a Hash in the example
for simplicity.

···

On Oct 9, 2:35 pm, Gary Wright <gwtm...@mac.com> wrote:

On Oct 9, 2007, at 1:50 PM, Brian Adkins wrote:

> c = MyClass.new('default')
> c[:foo] = 'foo'
> puts c[:foo]
> puts c[:test]
> c = MyClass.new {|obj,key| "default for #{key}" }
> puts c[:test]

This looks to me like you've just recreated features
of Hash. Might as well just use Hash.

* Brian Adkins <lojicdotcom@gmail.com> [10.10.2007]:

> This looks to me like you've just recreated features
> of Hash. Might as well just use Hash.

Presumably the OP had a reason for wanting an "array-like class"
instead of an actual Array or Hash. I only used a Hash in the example
for simplicity.

right! :slight_smile: Thanks fpr your example, I decided to chose the variant where
the block is given to the constructor. If there wasn't the syntax-problem
with the block on # i would have chosen that as it seems to be more
intuitive.

Anyway, thanks for the interesting discussion. It's nice to see how
different people target the same problem with different solutions.

Cheers,

Steph.

a) in practice a simple literal will not suffice
b) the result of the block should be assigned to the internal array at
the same step. (this was not part of my example code, sorry)

and

Anyway, thanks for the interesting discussion. It's nice to see how
different people target the same problem with different solutions.

Stephan, here's one more:

  c[:test] ||= (
    ...
  )

Gruß,
Pit

···

2007/10/10, Stephan Mueller <d454d@web.de>: