each with separator

Discussion in 'Ruby' started by Michael Neumann, Oct 20, 2004.

  1. 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
     
    Michael Neumann, Oct 20, 2004
    #1
    1. Advertising

  2. Michael Neumann

    Phil Tomson Guest

    In article <>,
    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
    >


    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
     
    Phil Tomson, Oct 20, 2004
    #2
    1. Advertising

  3. 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
     
    Charles Mills, Oct 20, 2004
    #3
  4. On Thu, Oct 21, 2004 at 03:54:17AM +0900, Phil Tomson wrote:
    > In article <>,
    > 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
    > >

    >
    > 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
     
    Michael Neumann, Oct 20, 2004
    #4
  5. Michael Neumann

    Jamis Buck Guest

    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

    http://www.jamisbuck.org/jamis
     
    Jamis Buck, Oct 20, 2004
    #5
  6. > 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, Oct 20, 2004
    #6
  7. > 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.
     
    Brian Candler, Oct 20, 2004
    #7
  8. "Michael Neumann" <> schrieb im Newsbeitrag
    news:...
    > 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
     
    Robert Klemme, Oct 21, 2004
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Blair
    Replies:
    1
    Views:
    411
    Eliyahu Goldin
    Aug 3, 2005
  2. Tjerk Wolterink

    xsl:for-each for each 3 elements problem

    Tjerk Wolterink, Nov 3, 2004, in forum: XML
    Replies:
    3
    Views:
    429
    Tjerk Wolterink
    Nov 3, 2004
  3. bjam
    Replies:
    3
    Views:
    4,052
  4. Pat Maddox
    Replies:
    6
    Views:
    158
    Marcin Mielżyński
    Jan 20, 2006
  5. ShaunJ
    Replies:
    12
    Views:
    341
    Peter J. Holzer
    Apr 17, 2008
Loading...

Share This Page