Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > FirstEachLast, an extension to the Enumerable module.

Reply
Thread Tools

FirstEachLast, an extension to the Enumerable module.

 
 
John Carter
Guest
Posts: n/a
 
      08-09-2004
I know somewhere is a collection of extensions to the enumerable module.

Is the rubygarden page
http://www.rubygarden.org/ruby?Stand...ons/Enumerable
the only one?

Anyway, I have just added FirstEachLast to that.

Use it like so...

require 'FirstEachLast'

def test_first_each_last(any_enumerable_object)
result = nil
count =
any_enumerable_object.first_each_last( proc {|first|
result = "First #{first}"
},
proc {|i|
result += " then #{i},"
},
proc {|last|
result += " and last of all #{last}"
},
proc {|| result = 'Empty!'}

puts count
puts result
end

test_first_each_last( [1,2,3,4])
test_first_each_last( [])

Will result in the following output...
4
First 1 then 2, then 3, and last of all 4
0
Empty!



John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : http://www.velocityreviews.com/forums/(E-Mail Removed)
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.


 
Reply With Quote
 
 
 
 
Robert Klemme
Guest
Posts: n/a
 
      08-09-2004

"John Carter" <(E-Mail Removed)> schrieb im Newsbeitrag
news(E-Mail Removed) .co.nz...
> I know somewhere is a collection of extensions to the enumerable module.
>
> Is the rubygarden page
> http://www.rubygarden.org/ruby?Stand...ons/Enumerable
> the only one?
>
> Anyway, I have just added FirstEachLast to that.
>
> Use it like so...
>
> require 'FirstEachLast'
>
> def test_first_each_last(any_enumerable_object)
> result = nil
> count =
> any_enumerable_object.first_each_last( proc {|first|
> result = "First

#{first}"
> },
> proc {|i|
> result += " then #{i},"
> },
> proc {|last|
> result += " and last of

all #{last}"
> },
> proc {|| result =

'Empty!'}
>
> puts count
> puts result
> end
>
> test_first_each_last( [1,2,3,4])
> test_first_each_last( [])
>
> Will result in the following output...
> 4
> First 1 then 2, then 3, and last of all 4
> 0
> Empty!


Nice output, but what do you need that for?

robert

 
Reply With Quote
 
 
 
 
John Carter
Guest
Posts: n/a
 
      08-09-2004
On Mon, 9 Aug 2004, Robert Klemme wrote:

> Nice output, but what do you need that for?


Mostly output to other tools, like gnuplot, or for reports. Most time
enum._to_a.join(",") does the job perfectly. However, there are the odd
time when that routine is just neat.


John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (E-Mail Removed)
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.


 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      08-10-2004

"John Carter" <(E-Mail Removed)> schrieb im Newsbeitrag
news(E-Mail Removed). co.nz...
> On Mon, 9 Aug 2004, Robert Klemme wrote:
>
> > Nice output, but what do you need that for?

>
> Mostly output to other tools, like gnuplot, or for reports. Most time
> enum._to_a.join(",") does the job perfectly. However, there are the odd
> time when that routine is just neat.


Hm, sounds to me like it was not general enough to include it in
Enumerable. Before I see that method I'd prefer to have size and empty?
in Enumerable. Just my 0.02 EUR...

Regards

robert

 
Reply With Quote
 
Martin DeMello
Guest
Posts: n/a
 
      08-10-2004
Robert Klemme <(E-Mail Removed)> wrote:
>
> Hm, sounds to me like it was not general enough to include it in
> Enumerable. Before I see that method I'd prefer to have size and empty?
> in Enumerable. Just my 0.02 EUR...


Can empty? be reliably implemented in terms of each for every
enumerable? I can't think of an obvious problem with it, but that
doesn't mean there isn't one.

martin
 
Reply With Quote
 
Alexander Kellett
Guest
Posts: n/a
 
      08-10-2004
On Tue, Aug 10, 2004 at 03:46:17PM +0900, Robert Klemme wrote:
> Hm, sounds to me like it was not general enough to include it in
> Enumerable. Before I see that method I'd prefer to have size and empty?
> in Enumerable. Just my 0.02 EUR...


personally the idea of having a method taking 4 procs
in the stdlib is just a bit strange. i'd prefer to see
a generic set of methods that are somehow mixedin to
the values that a Enumerable yields, therefore allowing
things like val.last? or first? or even a generic
val.enum_index to replace the each_with_index rubbish.
i've no idea how to solve this neatly unfortunately as
extend'ing the objects that are yield'ed of course will
have sideeffects... though most of the side effects that
will cause problems shouldn't happen at all, e.g people
calling is_a? a lot will have problems... i do this
and yet i don't have an exception to such an extension...
but this:

blah.each {
|element|
pos = blah.index element
puts blah[pos-1]
puts element
}

is soooooo uggllyyy... not to mention inefficient...
i'd just loooveeee to see a element.previous_in_enum...

cheers,
Alex


 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      08-10-2004

"Martin DeMello" <(E-Mail Removed)> schrieb im Newsbeitrag
newsW%Rc.68814$gE.48438@pd7tw3no...
> Robert Klemme <(E-Mail Removed)> wrote:
> >
> > Hm, sounds to me like it was not general enough to include it in
> > Enumerable. Before I see that method I'd prefer to have size and

empty?
> > in Enumerable. Just my 0.02 EUR...

>
> Can empty? be reliably implemented in terms of each for every
> enumerable? I can't think of an obvious problem with it, but that
> doesn't mean there isn't one.


Yes, that's possible IMHO. My suggestion would be:

module Enumerable
# O(1)
def empty?
each { return false }
true
end

# O(n)
def size
inject(0) {|s,| s+1}
end
end

Do you see any problems? There's only the little disadvantage that size
is O(n) while there are most likely more efficient implementations for
certain collections. But classes like Array and Hash provide their own
implementation anyway, which then overrides Enumerable#size. So there's
no drawback IMHO, only the advantage that you don't have to write it if
you have a collection that can determine its size only via a complete
iteration.

Kind regards

robert



 
Reply With Quote
 
Michael Neumann
Guest
Posts: n/a
 
      08-10-2004
Alexander Kellett wrote:
> On Tue, Aug 10, 2004 at 03:46:17PM +0900, Robert Klemme wrote:
>
>>Hm, sounds to me like it was not general enough to include it in
>>Enumerable. Before I see that method I'd prefer to have size and empty?
>>in Enumerable. Just my 0.02 EUR...

>
>
> personally the idea of having a method taking 4 procs
> in the stdlib is just a bit strange. i'd prefer to see
> a generic set of methods that are somehow mixedin to
> the values that a Enumerable yields, therefore allowing
> things like val.last? or first? or even a generic
> val.enum_index to replace the each_with_index rubbish.
> i've no idea how to solve this neatly unfortunately as
> extend'ing the objects that are yield'ed of course will
> have sideeffects... though most of the side effects that
> will cause problems shouldn't happen at all, e.g people
> calling is_a? a lot will have problems... i do this
> and yet i don't have an exception to such an extension...
> but this:
>
> blah.each {
> |element|
> pos = blah.index element
> puts blah[pos-1]
> puts element
> }
>
> is soooooo uggllyyy... not to mention inefficient...
> i'd just loooveeee to see a element.previous_in_enum...


Why not something like this (each_with_first_last does not exist!):

blah.each_with_first_last {|element, pos|
case pos
when :inside # or middle
...
when :first
...
when :last
...
end
}

Of course this would be somewhat slower than the approach with 4 procs,
as you have to test where you are, inside the loop. If you use the
approach above, make sure that you test for the common-case ("inside")
in the case-statement at the very beginning.

I know, you could also use each_with_index instead (this one exists!):

blah.each_with_index {|element, inx|
if inx == 0
# first
elsif inx < blah.size
# inside
else
# last
end
}

But that might not work for all Enumerable's.

Or maybe, an extended each_with_index2?:

blah.each_with_index2 {|element, inx, pos|
if pos == :first
...
}

Regards,

Michael


 
Reply With Quote
 
Michael Neumann
Guest
Posts: n/a
 
      08-10-2004
Michael Neumann wrote:
> I know, you could also use each_with_index instead (this one exists!):
>
> blah.each_with_index {|element, inx|
> if inx == 0
> # first
> elsif inx < blah.size


should be:

elsif inx < blah.size - 1

> # inside
> else
> # last
> end
> }
>



 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      08-10-2004

"Alexander Kellett" <(E-Mail Removed)> schrieb im Newsbeitrag
news:20040810090629.GA13191@loki...
> On Tue, Aug 10, 2004 at 03:46:17PM +0900, Robert Klemme wrote:
> > Hm, sounds to me like it was not general enough to include it in
> > Enumerable. Before I see that method I'd prefer to have size and

empty?
> > in Enumerable. Just my 0.02 EUR...

>
> personally the idea of having a method taking 4 procs
> in the stdlib is just a bit strange.


Agreed.

> i'd prefer to see
> a generic set of methods that are somehow mixedin to
> the values that a Enumerable yields, therefore allowing
> things like val.last? or first? or even a generic
> val.enum_index to replace the each_with_index rubbish.
> i've no idea how to solve this neatly unfortunately as
> extend'ing the objects that are yield'ed of course will
> have sideeffects... though most of the side effects that
> will cause problems shouldn't happen at all, e.g people
> calling is_a? a lot will have problems...


I strongly disagree. All sorts of problems arise from this approach:

- permanent modification of elements' types
- method name collisions
- member state collisions
- thread problems (1 instance used during two parallel iterations)

The index clearly belongs to the *iteration* and not the elements.
Possible solutions:

- yield not the instance but an iteration state that has the current item
as member
- use delegator (this avoids permanent changes but has still method
collision problems)

> i do this
> and yet i don't have an exception to such an extension...
> but this:
>
> blah.each {
> |element|
> pos = blah.index element
> puts blah[pos-1]
> puts element
> }
>
> is soooooo uggllyyy... not to mention inefficient...
> i'd just loooveeee to see a element.previous_in_enum...


First of all you don't cope with the first element properly. Then, there
are simple and efficient solutions available. To name some:

# for Arrays
blah.each_with_index {
|element, pos|
puts blah[pos-1] if pos != 0
puts element
}

NOTHING = Object.new
last = NOTHING
blah.each {
|element|
puts last if NOTHING != last
puts element
last = element
}

NOTHING = Object.new
blah.inject(NOTHING) {
|last, element|
puts last if NOTHING != last
puts element
element
}

NOTHING = Object.new
blah.inject(NOTHING) {
|last, element|
process last if NOTHING != last
element
}
process last if NOTHING != last


etc. depending on what you want to do.

Regards

robert

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Enumerable methods returning Enumerable (was: ruby-dev suumary 26862-26956) Daniel Sheppard Ruby 0 09-16-2005 02:40 AM
An addition to Array (or Enumerable)? Harry Ohlsen Ruby 11 12-29-2003 02:05 PM
Support for arbitrary iterators in Enumerable? Pierre-Charles David Ruby 2 11-20-2003 02:34 PM
Enumerable#inject is surprising me... Nathaniel Talbott Ruby 0 10-06-2003 11:29 PM
Why isn't Enumerable in StringIO? Jason Creighton Ruby 1 07-26-2003 05:14 AM



Advertisments