Enum_cons with tail

enum_cons chops off the tail of the collection, if it isn't evenly
divisble:

e.g.:
    (1..10).each_cons(3) {|a| p a}
    # outputs below
    [1, 2, 3]
    [2, 3, 4]
    [3, 4, 5]
    [4, 5, 6]
    [5, 6, 7]
    [6, 7, 8]
    [7, 8, 9]
    [8, 9, 10]

I'd also like to get:
[9, 10, nil]
[10, nil, nil]

Is there anyway to do this? (The collection is an opened File, too
large to load entirely in memory.)

I don't have a clear grasp as to how internally Ruby treats a File as
Enumerable.

Not that I am aware of. Two things come to mind. 1) You could append
nil items to your array to make it evenly divisable, 2) you could
extend arrary with your own each_cons that does what you want. Hope
this is at least some help. Perhaps others will know a more direct
way to accomplish what you seek.

Ken

···

On Feb 23, 11:50 am, "S. Robert James" <srobertja...@gmail.com> wrote:

enum_cons chops off the tail of the collection, if it isn't evenly
divisble:

e.g.:
    (1..10).each_cons(3) {|a| p a}
    # outputs below
    [1, 2, 3]
    [2, 3, 4]
    [3, 4, 5]
    [4, 5, 6]
    [5, 6, 7]
    [6, 7, 8]
    [7, 8, 9]
    [8, 9, 10]

I'd also like to get:
[9, 10, nil]
[10, nil, nil]

Is there anyway to do this? (The collection is an opened File, too
large to load entirely in memory.)

I don't have a clear grasp as to how internally Ruby treats a File as
Enumerable.

enum_cons chops off the tail of the collection, if it isn't evenly
divisble:

e.g.:
    (1..10).each_cons(3) {|a| p a}
    # outputs below
    [1, 2, 3]
    [2, 3, 4]
    [3, 4, 5]
    [4, 5, 6]
    [5, 6, 7]
    [6, 7, 8]
    [7, 8, 9]
    [8, 9, 10]

I'd also like to get:
[9, 10, nil]
[10, nil, nil]

You want semantics that are different from #each_cons. #each_cons will give you a sliding window of n elements from the collection (see you example output). This can be used to e.g. smooth a measurement curve.

Is there anyway to do this? (The collection is an opened File, too
large to load entirely in memory.)

Yes, see below (not the best implementation though). But maybe you want to change the algorithm that needs this. What are you trying to accomplish?

I don't have a clear grasp as to how internally Ruby treats a File as
Enumerable.

You can cook your own version:

require 'enumerator'
module Enumerable
   def sub_range(n)
     ar = inject() do |rg, x|
       rg.push x
       rg.shift if rg.length > n
       yield *rg if rg.length == n
       rg
     end
     (n-1).times do
       ar.push nil
       ar.shift if ar.length > n
       yield *ar if ar.length == n
     end
     self
   end

   def each_add(n,&b)
     each(&b)
     n.times { b[nil] }
     self
   end

   def sub_range2(n,&b)
     to_enum(:each_add, n-1).enum_cons(n).each {|a| b[*a]}
   end
end

irb(main):036:0> [1].sub_range(3){|*x| p x}
[1, nil, nil]
=> [1]
irb(main):037:0> [1,2,3].sub_range(3){|*x| p x}
[1, 2, 3]
[2, 3, nil]
[3, nil, nil]
=> [1, 2, 3]
irb(main):038:0> (1..10).sub_range(3){|*x| p x}
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
[9, 10, nil]
[10, nil, nil]
=> 1..10
irb(main):250:0> (1..1).sub_range2(3) {|*a| p a}
[1, nil, nil]
=> nil
irb(main):251:0> (1..10).sub_range2(3) {|*a| p a}
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
[9, 10, nil]
[10, nil, nil]
=> nil

Kind regards

  robert

···

On 23.02.2007 20:50, S. Robert James wrote:

I'm not sure how to do either of those. Like I said, this is a File,
not a true Array. (The magic of Ruby makes it Enumerable, but I
haven't mastered that magic yet.)

···

On Feb 23, 3:54 pm, "Kenosis" <keno...@gmail.com> wrote:

On Feb 23, 11:50 am, "S. Robert James" <srobertja...@gmail.com> wrote:

> enum_cons chops off the tail of the collection, if it isn't evenly
> divisble:

> e.g.:
> (1..10).each_cons(3) {|a| p a}
> # outputs below
> [1, 2, 3]
> [2, 3, 4]
> [3, 4, 5]
> [4, 5, 6]
> [5, 6, 7]
> [6, 7, 8]
> [7, 8, 9]
> [8, 9, 10]

> I'd also like to get:
> [9, 10, nil]
> [10, nil, nil]

> Is there anyway to do this? (The collection is an opened File, too
> large to load entirely in memory.)

> I don't have a clear grasp as to how internally Ruby treats a File as
> Enumerable.

Not that I am aware of. Two things come to mind. 1) You could append
nil items to your array to make it evenly divisable, 2) you could
extend arrary with your own each_cons that does what you want. Hope
this is at least some help. Perhaps others will know a more direct
way to accomplish what you seek.

Ken

It's not important whether the enumeration is done on a File or not. As long
as the object has a meaningful "each" method, you can enumerate it any way
you want.
Your modified enum_const can be accomplished by wrapping the original
#each_cons in a home-made method. So first, here is a wrapping method that
just does nothing more than the original :

module Enumerable
  def each_cons_2(n, &block)
    each_cons(n) {|a|
      block.call(a)
    }
  end
end

Then, we just need to get the last value returned by the called block, to be
able to manually continue the iteration the way you want (adding nils) :

module Enumerable
  def each_cons_2(n, &block)
    a = nil
    each_cons(n) {|a|
      block.call(a)
    }
    (n-1).times { a.shift; a << nil; yield a }
    nil
  end
end

The trick here, is to declare the variable "a" before the block, so that this
a is the same than the |a| in the block. Doing so, we end up after the block
with "a" containing the value returned by the last call of the block (ie, the
array [8,9,10] in your example). Then, we continue the iteration, modifying
and yielding this array n-1 times.

(1..10).each_cons_2(3) {|a| p a}
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
[9, 10, nil]
[10, nil, nil]
=> nil

···

Le vendredi 23 février 2007 22:25, S. Robert James a écrit :

I'm not sure how to do either of those. Like I said, this is a File,
not a true Array. (The magic of Ruby makes it Enumerable, but I
haven't mastered that magic yet.)

On Feb 23, 3:54 pm, "Kenosis" <keno...@gmail.com> wrote:
> On Feb 23, 11:50 am, "S. Robert James" <srobertja...@gmail.com> wrote:
> > enum_cons chops off the tail of the collection, if it isn't evenly
> > divisble:
> >
> > e.g.:
> > (1..10).each_cons(3) {|a| p a}
> > # outputs below
> > [1, 2, 3]
> > [2, 3, 4]
> > [3, 4, 5]
> > [4, 5, 6]
> > [5, 6, 7]
> > [6, 7, 8]
> > [7, 8, 9]
> > [8, 9, 10]
> >
> > I'd also like to get:
> > [9, 10, nil]
> > [10, nil, nil]
> >
> > Is there anyway to do this? (The collection is an opened File, too
> > large to load entirely in memory.)
> >
> > I don't have a clear grasp as to how internally Ruby treats a File as
> > Enumerable.
>
> Not that I am aware of. Two things come to mind. 1) You could append
> nil items to your array to make it evenly divisable, 2) you could
> extend arrary with your own each_cons that does what you want. Hope
> this is at least some help. Perhaps others will know a more direct
> way to accomplish what you seek.
>
> Ken

--
Olivier Renaud

Thanks for the excellent explanation and code.

One question I have is: why is each_cons inconsistent vis-a-vis
each_slice? each_slice returns the trailing items, padded with nils -
why doesn't each cons do the same?

···

On Feb 23, 7:32 pm, Olivier Renaud <o.ren...@laposte.net> wrote:

It's not important whether the enumeration is done on a File or not. As long
as the object has a meaningful "each" method, you can enumerate it any way
you want.
Your modified enum_const can be accomplished by wrapping the original
#each_cons in a home-made method. So first, here is a wrapping method that
just does nothing more than the original :

module Enumerable
  def each_cons_2(n, &block)
    each_cons(n) {|a|
      block.call(a)
    }
  end
end

Then, we just need to get the last value returned by the called block, to be
able to manually continue the iteration the way you want (adding nils) :

module Enumerable
  def each_cons_2(n, &block)
    a = nil
    each_cons(n) {|a|
      block.call(a)
    }
    (n-1).times { a.shift; a << nil; yield a }
    nil
  end
end

The trick here, is to declare the variable "a" before the block, so that this
a is the same than the |a| in the block. Doing so, we end up after the block
with "a" containing the value returned by the last call of the block (ie, the
array [8,9,10] in your example). Then, we continue the iteration, modifying
and yielding this array n-1 times.

(1..10).each_cons_2(3) {|a| p a}
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
[9, 10, nil]
[10, nil, nil]
=> nil

--
Olivier Renaud

Hmm, are you sure of that ? The last object yielded by each_slice is an array
containing the trailling items, but not padded with nils :

irb(main):004:0> (1..10).each_cons(3) {|i| p i}
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
=> nil
irb(main):005:0> (1..10).each_slice(3) {|i| p i}
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]
=> nil

Of course, if you try to access the elements of the array individually, you
will get the default value for the "empty" indices : nil.

irb(main):006:0> (1..10).each_slice(3) {|i| p [i[0], i[1], i[2]]}
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10, nil, nil]
=> nil

There's no inconsistency, then.

I hope I answered your question :slight_smile:

···

Le dimanche 25 février 2007 03:15, S. Robert James a écrit :

Thanks for the excellent explanation and code.

One question I have is: why is each_cons inconsistent vis-a-vis
each_slice? each_slice returns the trailing items, padded with nils -
why doesn't each cons do the same?

--
Olivier Renaud