Array::index and rindex operator

Hi,

the Array::index and rindex functions

   arr.index( anObject ) -> anInteger or nil
   Return the index of the first/last object in arr such that the
   object == anObject. Returns nil if no match is found.

Wouldn't it be better to have

   such that anObject === object ?

regards
Hadmut

Hadmut Danisch <nospam@danisch.de> writes:

Hi,

the Array::index and rindex functions

   arr.index( anObject ) -> anInteger or nil
   Return the index of the first/last object in arr such that the
   object == anObject. Returns nil if no match is found.

Wouldn't it be better to have

   such that anObject === object ?

I'm not so sure about that; #=== is primarily meant for case
statements, and very few methods use it like the way you propose. It
would also separate it from other Array/Enumerable methods like #find,
#delete, #include?, etc.

Sometimes though I wish that #index could take a block (i.e., return
the first index for which the block is true). Unless I'm overlooking
something, there doesn't seem to be a method that does this. I don't
know what others think of the idea, but that would probably go some
way towards your goal.

George Ogata wrote:

Hadmut Danisch <nospam@danisch.de> writes:

Hi,

the Array::index and rindex functions

  arr.index( anObject ) -> anInteger or nil
  Return the index of the first/last object in arr such that the
  object == anObject. Returns nil if no match is found.

Wouldn't it be better to have

  such that anObject === object ?

I'm not so sure about that; #=== is primarily meant for case
statements, and very few methods use it like the way you propose. It
would also separate it from other Array/Enumerable methods like #find,
#delete, #include?, etc.

Sometimes though I wish that #index could take a block (i.e., return
the first index for which the block is true). Unless I'm overlooking
something, there doesn't seem to be a method that does this. I don't
know what others think of the idea, but that would probably go some
way towards your goal.

Enumerable#grep does use #===, so maybe what the OP wants is something like grep but instead of returning the matching value returns the index.

Hi,

Sometimes though I wish that #index could take a block (i.e., return
the first index for which the block is true). Unless I'm overlooking
something, there doesn't seem to be a method that does this. I don't
know what others think of the idea, but that would probably go some
way towards your goal.

I think that would be nice. I would vote for it.
You could do this with enumerator, but it is not so compact:

require "enumerator"

a = %w(one two three)
a.enum_for(:each_with_index).find { |s, i| s == "two" }[1]
=> 1

Regards,
Kristof

···

On Tue, 06 Jul 2004 09:42:41 +1000, George Ogata wrote:

"Kristof Bastiaensen" <kristof@vleeuwen.org> schrieb im Newsbeitrag
news:pan.2004.07.06.10.51.04.229075@vleeuwen.org...

Hi,

> Sometimes though I wish that #index could take a block (i.e., return
> the first index for which the block is true). Unless I'm overlooking
> something, there doesn't seem to be a method that does this. I don't
> know what others think of the idea, but that would probably go some
> way towards your goal.

I think that would be nice. I would vote for it.
You could do this with enumerator, but it is not so compact:

require "enumerator"

a = %w(one two three)
a.enum_for(:each_with_index).find { |s, i| s == "two" }[1]
=> 1

Another solution:

a.inject(0) {|i,s| break i if "two" == s; idx+1}

#inject is just great!

Regards

    robert

···

On Tue, 06 Jul 2004 09:42:41 +1000, George Ogata wrote:

Hi --

···

On Tue, 6 Jul 2004, Robert Klemme wrote:

"Kristof Bastiaensen" <kristof@vleeuwen.org> schrieb im Newsbeitrag
news:pan.2004.07.06.10.51.04.229075@vleeuwen.org...
> Hi,
>
> On Tue, 06 Jul 2004 09:42:41 +1000, George Ogata wrote:
>
> > Sometimes though I wish that #index could take a block (i.e., return
> > the first index for which the block is true). Unless I'm overlooking
> > something, there doesn't seem to be a method that does this. I don't
> > know what others think of the idea, but that would probably go some
> > way towards your goal.
>
> I think that would be nice. I would vote for it.
> You could do this with enumerator, but it is not so compact:
>
> require "enumerator"
>
> a = %w(one two three)
> a.enum_for(:each_with_index).find { |s, i| s == "two" }[1]
> => 1

Another solution:

a.inject(0) {|i,s| break i if "two" == s; idx+1}

The problem though (aside from 'idx' for 'i' :slight_smile: is that you always
get a number, even if there's no element found:

  [1,2,3].inject(0) {|i,s| break i if "two" == s; i + 1}

  # => 3

David

--
David A. Black
dblack@wobblini.net

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.44.0407060521080.8383-100000@wobblini...

Hi --

>
> "Kristof Bastiaensen" <kristof@vleeuwen.org> schrieb im Newsbeitrag
> news:pan.2004.07.06.10.51.04.229075@vleeuwen.org...
> > Hi,
> >
> >
> > > Sometimes though I wish that #index could take a block (i.e.,

return

> > > the first index for which the block is true). Unless I'm

overlooking

> > > something, there doesn't seem to be a method that does this. I

don't

> > > know what others think of the idea, but that would probably go

some

> > > way towards your goal.
> >
> > I think that would be nice. I would vote for it.
> > You could do this with enumerator, but it is not so compact:
> >
> > require "enumerator"
> >
> > a = %w(one two three)
> > a.enum_for(:each_with_index).find { |s, i| s == "two" }[1]
> > => 1
>
> Another solution:
>
> a.inject(0) {|i,s| break i if "two" == s; idx+1}

The problem though (aside from 'idx' for 'i' :slight_smile:

Thanks for that correction!

is that you always
get a number, even if there's no element found:

  [1,2,3].inject(0) {|i,s| break i if "two" == s; i + 1}

  # => 3

True. The approach is betters suited to a method implementation:

module Enumerable
  def index_2(obj=nil, &b)
    b = lambda {|x| obj == x} unless b
    inject(0) {|i,el| return i if b.call(el); i+1}
    nil
  end
end

Thx for correcting my sloppyness.

    robert

···

On Tue, 6 Jul 2004, Robert Klemme wrote:
> > On Tue, 06 Jul 2004 09:42:41 +1000, George Ogata wrote:

Hi --

True. The approach is betters suited to a method implementation:

module Enumerable
  def index_2(obj=nil, &b)
    b = lambda {|x| obj == x} unless b
    inject(0) {|i,el| return i if b.call(el); i+1}
    nil
  end
end

I agree. My version, for what it's worth, was:

  module Enumerable
    def b_index
      each_with_index {|e,i| return i if yield(e) }
      nil
    end
  end

I decided to make it orthogonal to #index, rather than a superset of
it, on the theory that it would be good to avoid making it legal to
give both an argument and a block. I also like #each_with_index
here, rather than #inject, only because it does more of the work and
eliminates the need for manually incrementing an index. I haven't
benchmarked them though.

David

···

On Tue, 6 Jul 2004, Robert Klemme wrote:

--
David A. Black
dblack@wobblini.net

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.44.0407060654550.23516-100000@wobblini...

Hi --

> True. The approach is betters suited to a method implementation:
>
> module Enumerable
> def index_2(obj=nil, &b)
> b = lambda {|x| obj == x} unless b
> inject(0) {|i,el| return i if b.call(el); i+1}
> nil
> end
> end

I agree. My version, for what it's worth, was:

  module Enumerable
    def b_index
      each_with_index {|e,i| return i if yield(e) }
      nil
    end
  end

I decided to make it orthogonal to #index, rather than a superset of
it, on the theory that it would be good to avoid making it legal to
give both an argument and a block.

Might be best to just enhance #index with the block variant.

I also like #each_with_index
here, rather than #inject, only because it does more of the work and
eliminates the need for manually incrementing an index. I haven't
benchmarked them though.

True. I was blind for that because of my strong #inject affection... :slight_smile:

Regards

    robert

···

On Tue, 6 Jul 2004, Robert Klemme wrote:

Hi --

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.44.0407060654550.23516-100000@wobblini...
> Hi --
>
>
> > True. The approach is betters suited to a method implementation:
> >
> > module Enumerable
> > def index_2(obj=nil, &b)
> > b = lambda {|x| obj == x} unless b
> > inject(0) {|i,el| return i if b.call(el); i+1}
> > nil
> > end
> > end
>
> I agree. My version, for what it's worth, was:
>
> module Enumerable
> def b_index
> each_with_index {|e,i| return i if yield(e) }
> nil
> end
> end
>
> I decided to make it orthogonal to #index, rather than a superset of
> it, on the theory that it would be good to avoid making it legal to
> give both an argument and a block.

Might be best to just enhance #index with the block variant.

What stumped me was: given this:

  ary.index(x) {|e| <condition> }

which behavior would you expect? Or would it raise an exception? I
couldn't decide which would be best, which is what led me to a new
method. I can't remember whether there are any methods that protest
if you give an argument and a block.... I have a memory that there
are one or two, but I'm not sure what they are.

David

···

On Wed, 7 Jul 2004, Robert Klemme wrote:

> On Tue, 6 Jul 2004, Robert Klemme wrote:

--
David A. Black
dblack@wobblini.net

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.44.0407060841010.12255-100000@wobblini...

Hi --

>
> "David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
> news:Pine.LNX.4.44.0407060654550.23516-100000@wobblini...
> > Hi --
> >
> >
> > > True. The approach is betters suited to a method implementation:
> > >
> > > module Enumerable
> > > def index_2(obj=nil, &b)
> > > b = lambda {|x| obj == x} unless b
> > > inject(0) {|i,el| return i if b.call(el); i+1}
> > > nil
> > > end
> > > end
> >
> > I agree. My version, for what it's worth, was:
> >
> > module Enumerable
> > def b_index
> > each_with_index {|e,i| return i if yield(e) }
> > nil
> > end
> > end
> >
> > I decided to make it orthogonal to #index, rather than a superset of
> > it, on the theory that it would be good to avoid making it legal to
> > give both an argument and a block.
>
> Might be best to just enhance #index with the block variant.

What stumped me was: given this:

  ary.index(x) {|e| <condition> }

which behavior would you expect? Or would it raise an exception? I
couldn't decide which would be best, which is what led me to a new
method.

#index simply returns nil.

I can't remember whether there are any methods that protest
if you give an argument and a block.... I have a memory that there
are one or two, but I'm not sure what they are.

Well, what implication would that have? Do you mean to say it's not
common in Ruby to have methods that either accept a block or an argument?

Regards

    robert

···

On Wed, 7 Jul 2004, Robert Klemme wrote:
> > On Tue, 6 Jul 2004, Robert Klemme wrote:

Robert Klemme wrote:

I can't remember whether there are any methods that protest
if you give an argument and a block.... I have a memory that there
are one or two, but I'm not sure what they are.

Well, what implication would that have? Do you mean to say it's not
common in Ruby to have methods that either accept a block or an argument?

I think it's common to accept a block or an arg, but it raises the
question of what to do when *both* are given:

   1. raise an exception
   2. do something meaningful

And I'm not sure whether most core methods do (1) or (2).

Hal

Hi --

> > Might be best to just enhance #index with the block variant.
>
> What stumped me was: given this:
>
> ary.index(x) {|e| <condition> }
>
> which behavior would you expect? Or would it raise an exception? I
> couldn't decide which would be best, which is what led me to a new
> method.

#index simply returns nil.

I think we're talking about different things. What I meant is: if you
extend/enhance #index to take a block, and then you give it a block
*and* an argument, there's an ambiguity (two possible behaviors):

  a = %w{ a b c d }
  i = a.enhanced_index("b") {|x| x == "c" }

Would i be 1 or 2? Or would it raise an exception, because it's
ambiguous? (It's different from, say, #inject, where there's both an
argument and a block but they have a direct connection with each
other.)

My conclusion was that it would be better not to allow this to be
legal, either by enforcing the choice in #index or creating a second
method.

David

···

On Wed, 7 Jul 2004, Robert Klemme wrote:

--
David A. Black
dblack@wobblini.net

Hi --

Robert Klemme wrote:
>> I can't remember whether there are any methods that protest
>>if you give an argument and a block.... I have a memory that there
>>are one or two, but I'm not sure what they are.
>
> Well, what implication would that have? Do you mean to say it's not
> common in Ruby to have methods that either accept a block or an argument?

I think it's common to accept a block or an arg, but it raises the
question of what to do when *both* are given:

   1. raise an exception
   2. do something meaningful

And I'm not sure whether most core methods do (1) or (2).

I still have this nagging memory of a method that raises an exception
when given both, but I can't pin it down.

The ones I can think of that don't just ignore the block (which of
course any method will do if it doesn't yield) seem to call the block
either with the argument (#inject) or with the result of a calculation
(File.open).

David

···

On Wed, 7 Jul 2004, Hal Fulton wrote:

--
David A. Black
dblack@wobblini.net

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.44.0407060917190.8001-100000@wobblini...

Hi --

> > > Might be best to just enhance #index with the block variant.
> >
> > What stumped me was: given this:
> >
> > ary.index(x) {|e| <condition> }
> >
> > which behavior would you expect? Or would it raise an exception? I
> > couldn't decide which would be best, which is what led me to a new
> > method.
>
> #index simply returns nil.

I think we're talking about different things. What I meant is: if you
extend/enhance #index to take a block, and then you give it a block
*and* an argument, there's an ambiguity (two possible behaviors):

  a = %w{ a b c d }
  i = a.enhanced_index("b") {|x| x == "c" }

Would i be 1 or 2? Or would it raise an exception, because it's
ambiguous? (It's different from, say, #inject, where there's both an
argument and a block but they have a direct connection with each
other.)

I'd certainly have it throw an exception.

My conclusion was that it would be better not to allow this to be
legal, either by enforcing the choice in #index or creating a second
method.

Yes, enforce the choice would be my preferred solution.

Regards

    robert

···

On Wed, 7 Jul 2004, Robert Klemme wrote:

I still have this nagging memory of a method that raises an exception
when given both, but I can't pin it down.

svg% ruby -e 'Hash.new("aa") {}'
-e:1:in `initialize': wrong number of arguments (ArgumentError)
        from -e:1:in `new'
        from -e:1
svg%

svg% ruby -e 'instance_eval("aa") {} '
-e:1:in `instance_eval': wrong number of arguments (1 for 0) (ArgumentError)
        from -e:1
svg%

Guy Decoux

ts <decoux@moulon.inra.fr> writes:

> I still have this nagging memory of a method that raises an exception
> when given both, but I can't pin it down.

svg% ruby -e 'Hash.new("aa") {}'
-e:1:in `initialize': wrong number of arguments (ArgumentError)
        from -e:1:in `new'
        from -e:1
svg%

svg% ruby -e 'instance_eval("aa") {} '
-e:1:in `instance_eval': wrong number of arguments (1 for 0) (ArgumentError)
        from -e:1

Interestingly:

g@crash:~$ ruby -e 'Array.new(1, 1){1}'
-e:1: warning: block supersedes default value argument

But to answer David( Black)'s concern, I think the only sane choice is
to raise an exception when both are given. It's ambiguous, so it's an
error, right?

Hi --

ts <decoux@moulon.inra.fr> writes:

>
> > I still have this nagging memory of a method that raises an exception
> > when given both, but I can't pin it down.
>
> svg% ruby -e 'Hash.new("aa") {}'
> -e:1:in `initialize': wrong number of arguments (ArgumentError)
> from -e:1:in `new'
> from -e:1
> svg%
>
> svg% ruby -e 'instance_eval("aa") {} '
> -e:1:in `instance_eval': wrong number of arguments (1 for 0) (ArgumentError)
> from -e:1

Interestingly:

g@crash:~$ ruby -e 'Array.new(1, 1){1}'
-e:1: warning: block supersedes default value argument

But to answer David( Black)'s concern, I think the only sane choice is
to raise an exception when both are given. It's ambiguous, so it's an
error, right?

It's an interesting problem, since the existence of #block_given?
suggests that branching on the presence/absence of a block is legit,
though these instances show that for this purpose it can be rather
messy.

I tend to favor the exception approach, though one could also argue
for a left-to-right logic: if a method needs an argument xor a block,
then once it gets an argument, let it be satisfied and ignore the
block (which is also a legit and indeed default action). One would
then have to be careful not to have stray useless blocks, but that's
already the case; one already could, but presumably would not, do
this:

  /blah/.match(str) { puts "useless block!" }

David

···

On Wed, 7 Jul 2004, George Ogata wrote:

--
David A. Black
dblack@wobblini.net

"David A. Black" <dblack@wobblini.net> writes:

But to answer David( Black)'s concern, I think the only sane choice is
to raise an exception when both are given. It's ambiguous, so it's an
error, right?

It's an interesting problem, since the existence of #block_given?
suggests that branching on the presence/absence of a block is legit,
though these instances show that for this purpose it can be rather
messy.

I tend to favor the exception approach, though one could also argue
for a left-to-right logic: if a method needs an argument xor a block,
then once it gets an argument, let it be satisfied and ignore the
block (which is also a legit and indeed default action). One would
then have to be careful not to have stray useless blocks, but that's
already the case; one already could, but presumably would not, do
this:

  /blah/.match(str) { puts "useless block!" }

Well, one could also counter-argue that in the useless-block cases
it's not ambiguous, though I think ideally, ruby should still
complain. I guess it doesn't due to a combination of ruby's dynamic
nature (it can't determine for itself whether or not a block might be
needed at compile time, and it's inefficient to check it at runtime),
and ruby's "no declarations" philosophy (i.e., no declaring "I take
blocks" in the method definition). Perhaps also because a useless
block isn't an oft-made mistake.

As for the "left-to-right" view, there's no precedence for this being
silently ignored in the presence of ambiguity, is there?

Hi --

"David A. Black" <dblack@wobblini.net> writes:

>> But to answer David( Black)'s concern, I think the only sane choice is
>> to raise an exception when both are given. It's ambiguous, so it's an
>> error, right?
>
> It's an interesting problem, since the existence of #block_given?
> suggests that branching on the presence/absence of a block is legit,
> though these instances show that for this purpose it can be rather
> messy.
>
> I tend to favor the exception approach, though one could also argue
> for a left-to-right logic: if a method needs an argument xor a block,
> then once it gets an argument, let it be satisfied and ignore the
> block (which is also a legit and indeed default action). One would
> then have to be careful not to have stray useless blocks, but that's
> already the case; one already could, but presumably would not, do
> this:
>
> /blah/.match(str) { puts "useless block!" }

Well, one could also counter-argue that in the useless-block cases
it's not ambiguous, though I think ideally, ruby should still
complain. I guess it doesn't due to a combination of ruby's dynamic
nature (it can't determine for itself whether or not a block might be
needed at compile time, and it's inefficient to check it at runtime),
and ruby's "no declarations" philosophy (i.e., no declaring "I take
blocks" in the method definition). Perhaps also because a useless
block isn't an oft-made mistake.

I'd rather not have a warning in the general case (i.e., imposed by
the interpreter rather than by the method's own logic). One might
have something like:

  def x
    @cached ||= yield * 10
  end

where the yielding happens conditionally. (Classically non-wonderful
example, but still :slight_smile:

As for the "left-to-right" view, there's no precedence for this being
silently ignored in the presence of ambiguity, is there?

I don't think any existing methods do this; I'm really just
speculating about whether or not it would make sense to write a method
that way. I think it would, at an abstract level, but having the
method intervene if one calls it ambiguously also makes sense, perhaps
more sense at the practical level.

David

···

On Wed, 7 Jul 2004, George Ogata wrote:

--
David A. Black
dblack@wobblini.net