Indexers and blocks?

  • Thread starter Stephan Mueller
  • Start date
S

Stephan Mueller

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.
 
D

David A. Black

Hi --

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!
 
S

Stephan Mueller

Hi,

* David A. Black said:
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.
 
R

Robert Dober

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).
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
 
S

Stephan Mueller

* Robert Dober said:
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... :)


Cheers,

Steph.
 
B

Brian Adkins

* Robert Dober said:
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 [email protected]_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]
 
G

Gary Wright

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.

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
 
B

Brian Adkins

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.

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.
 
S

Stephan Mueller

* Brian Adkins said:
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! :) 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.
 
P

Pit Capitain

2007/10/10 said:
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] ||=3D (
...
)

Gru=DF,
Pit
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,262
Messages
2,571,054
Members
48,769
Latest member
Clifft

Latest Threads

Top