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.

On Feb 23, 11:50 am, “S. Robert J.” [email protected] wrote:

[5, 6, 7]

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

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.)

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

(1..10).each_cons(3) {|a| p a}

I’d also like to get:
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

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

[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

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?

Le dimanche 25 février 2007 03:15, S. Robert J. 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?

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: