[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

each with separator

Michael Neumann

10/20/2004 6:06:00 PM

Hi,

I know we had a similar thread in the past about first/last-element
detection.

In Smalltalk there's #do:separateBy: which basically works like
each+join combined, but in an imperative fashion.

Now I tried to implement:

[1,2,3].separate_by { puts "-" }.each {|obj|
puts obj
}

desired output:

1
-
2
-
3

This is how I thought it would work:

require 'generator'

module Enumerable
def separate_by(&block)
last = size - 1
Generator.new {|g|
each_with_index {|obj, inx|
g.yield obj
block.call if inx < last
}
}
end
end

But the output is as follows:

-
1
-
2
3

g.yield does not actually stop until an item is consumed.

Any idea how to solve this?

Regards,

Michael


7 Answers

ptkwt

10/20/2004 6:27:00 PM

0

In article <20041020180532.GA1126@miya.intranet.ntecs.de>,
Michael Neumann <mneumann@ntecs.de> wrote:
>Hi,
>
>I know we had a similar thread in the past about first/last-element
>detection.
>
>In Smalltalk there's #do:separateBy: which basically works like
>each+join combined, but in an imperative fashion.
>
>Now I tried to implement:
>
> [1,2,3].separate_by { puts "-" }.each {|obj|
> puts obj
> }
>
>desired output:
>
> 1
> -
> 2
> -
> 3
>

Well, it's not exactly the general-purpose solution you're looking for,
but I would do it like this:

[1,2,3].join("\n-\n").each {|i| puts i }

1
-
2
-
3


Phil

Charles Mills

10/20/2004 6:58:00 PM

0

On Oct 20, 2004, at 11:05 AM, Michael Neumann wrote:

> Hi,
>
> I know we had a similar thread in the past about first/last-element
> detection.
>
> In Smalltalk there's #do:separateBy: which basically works like
> each+join combined, but in an imperative fashion.
>
> Now I tried to implement:
>
> [1,2,3].separate_by { puts "-" }.each {|obj|
> puts obj
> }
>
> desired output:
>
> 1
> -
> 2
> -
> 3
>

Here is another solution your probably not interested in:

module Enumerable
def separate_by(&block)
ary = []
each_with_index do |obj, i|
ary << block.call if i > 0
ary << obj
end
ary
end
end

puts [1,2,3].separate_by { "-" }.join("\n")

## output:
1
-
2
-
3

-Charlie



Michael Neumann

10/20/2004 7:10:00 PM

0

On Thu, Oct 21, 2004 at 03:54:17AM +0900, Phil Tomson wrote:
> In article <20041020180532.GA1126@miya.intranet.ntecs.de>,
> Michael Neumann <mneumann@ntecs.de> wrote:
> >Hi,
> >
> >I know we had a similar thread in the past about first/last-element
> >detection.
> >
> >In Smalltalk there's #do:separateBy: which basically works like
> >each+join combined, but in an imperative fashion.
> >
> >Now I tried to implement:
> >
> > [1,2,3].separate_by { puts "-" }.each {|obj|
> > puts obj
> > }
> >
> >desired output:
> >
> > 1
> > -
> > 2
> > -
> > 3
> >
>
> Well, it's not exactly the general-purpose solution you're looking for,
> but I would do it like this:
>
> [1,2,3].join("\n-\n").each {|i| puts i }

Yes of course, but it's not imperative and I'm looking for an imperative
version like the Smalltalk one.

Regards,

Michael


Jamis Buck

10/20/2004 7:13:00 PM

0

Charles Mills wrote:
> On Oct 20, 2004, at 11:05 AM, Michael Neumann wrote:
>
>> Hi,
>>
>> I know we had a similar thread in the past about first/last-element
>> detection.
>>
>> In Smalltalk there's #do:separateBy: which basically works like
>> each+join combined, but in an imperative fashion.
>>
>> Now I tried to implement:
>>
>> [1,2,3].separate_by { puts "-" }.each {|obj|
>> puts obj
>> }
>>
>> desired output:
>>
>> 1
>> -
>> 2
>> -
>> 3
>>
>
> Here is another solution your probably not interested in:
>
> module Enumerable
> def separate_by(&block)
> ary = []
> each_with_index do |obj, i|
> ary << block.call if i > 0
> ary << obj
> end
> ary
> end
> end
>

Another esoteric solution, which came to me while reading the above
solution:

module Enumerable
def separate_by(&block)
class << block; def to_s; call; end; end
inject( [] ) { |a,i| a << block unless a.empty?; a << i }
end
end

puts [ 1, 2, 3 ].separate_by { "-" }

- Jamis

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck...


Brian Candler

10/20/2004 7:48:00 PM

0

> Yes of course, but it's not imperative and I'm looking for an imperative
> version like the Smalltalk one.

My inclination would be to start simple:

module Enumerable
def each_with_separator(sepblk)
first = true
each do |item|
sepblk.call unless first
first = false
yield item
end
end
end

[1,2,3].each_with_separator(proc { puts "-" }) {|obj| puts obj }

But it's admittedly a bit ugly passing two blocks in this way. If you want
you can create an intermediate object which holds the separator block and
performs the iteration:

module Enumerable
def separate_by(&blk)
Separator.new(self,blk)
end
class Separator
include Enumerable # eat your own tail :-)
def initialize(thing,sepblk)
@thing = thing
@sepblk = sepblk
end
def each
first = true
@thing.each do |item|
@sepblk.call unless first
first = false
yield item
end
end
end
end

[1,2,3].separate_by { puts "-" }.each {|obj| puts obj}

I'm not sure if that's a general enough pattern for you. For example, an
Enumerable::Separator is enumerable, but you can't use 'collect' to collect
the objects and the separators.

Perhaps you want to 'yield' the separator instead?

module Enumerable
def separate_by2(separator)
Separator2.new(self,separator)
end
class Separator2
include Enumerable
def initialize(thing,separator)
@thing = thing
@separator = separator
end
def each
first = true
@thing.each do |item|
yield @separator unless first
first = false
yield item
end
end
end
end

[1,2,3].separate_by2("-").each {|obj| puts obj}

a = [1,2,3].separate_by2("-").collect {|obj| obj * 2}
#=> returns [2, "--", 4, "--", 6]

Regards,

Brian.


Brian Candler

10/20/2004 8:03:00 PM

0

> My inclination would be to start simple:
>
> module Enumerable

I suppose, from what I was saying before about it being impolite to pollute
system classes (giving rise to potential name conflicts), that I ought to
separate it out of Enumerable.

module SuperEnumerable # choose a unique name here
def separate_by(separator)
Separator.new(self,separator)
end
class Separator
include Enumerable
def initialize(thing,separator)
@thing = thing
@separator = separator
end
def each
first = true
@thing.each do |item|
yield @separator unless first
first = false
yield item
end
end
end
end

a = [1,2,3]
a.extend SuperEnumerable
a.separate_by("-").each {|obj| puts obj}
b = a.separate_by("-").collect {|obj| obj * 2}
p b

You can always include SuperEnumerable in class Array if you wish, but
you're not required to.

Regards,

Brian.


Robert Klemme

10/21/2004 8:02:00 AM

0


"Michael Neumann" <mneumann@ntecs.de> schrieb im Newsbeitrag
news:20041020180532.GA1126@miya.intranet.ntecs.de...
> Hi,
>
> I know we had a similar thread in the past about first/last-element
> detection.
>
> In Smalltalk there's #do:separateBy: which basically works like
> each+join combined, but in an imperative fashion.
>
> Now I tried to implement:
>
> [1,2,3].separate_by { puts "-" }.each {|obj|
> puts obj
> }
>
> desired output:
>
> 1
> -
> 2
> -
> 3
>
> This is how I thought it would work:
>
> require 'generator'
>
> module Enumerable
> def separate_by(&block)
> last = size - 1

Don't use size! It's not guarantted to be present in each Enumerable.

> Generator.new {|g|
> each_with_index {|obj, inx|
> g.yield obj
> block.call if inx < last
> }
> }
> end
> end
>
> But the output is as follows:
>
> -
> 1
> -
> 2
> 3
>
> g.yield does not actually stop until an item is consumed.
>
> Any idea how to solve this?

Yes. Enumerable#inject is your friend:

module Enumerable
def separate_by_1(block)
inject(false) do |flag, x|
block.call if flag
yield x
true
end

self
end

class Separator < Struct.new(:enum, :sep)
include Enumerable

def each
enum.inject(false) do |flag, x|
sep.call if flag
yield x
true
end

enum
end
end

def separate_by_2(&block) Separator.new(self, block) end
end

>> [1,2,3].separate_by_1( lambda { puts "-" } ) {|obj|
?> puts obj
>> }
1
-
2
-
3
=> [1, 2, 3]
>> [1,2,3].separate_by_2 { puts "-" }.each {|obj|
?> puts obj
>> }
1
-
2
-
3
=> [1, 2, 3]

Kind regards

robert