Detecting if the first element might in some circumstances server the same
purpose and it’s easy done without Thread tricks:
def print_format(enum)
first = true
str = “[”
enum.each do |e|
if first
first = false
else
str << ", "
end
str << e.to_s
end
str << “]”
str
end
a=[10, 30, “foo”]
puts print_format(a)
robert
“Carlos” angus@quovadis.com.ar schrieb im Newsbeitrag
news:20030530113250.GB1403@quovadis.com.ar…
Is there any built in functionality for iteration that will allow me to
detect when I am on the last element? This would not be hard for an
Array,
using array.length, but what about a Hash? How would I know when I am
at
the last element?
Here is a module that implements it, but you should include it in every
class where you want #each to behave that way :-/. (And some #each-like
methods (for example, String#each_byte, don’t work at all…)
oe.rb:
#!/usr/bin/ruby
class Object
def last?
Thread.current.last_iteration?
end
private :last?
end
class Thread
def last_iteration?
@last_iteration
end
end
def each(*a,&b)
element = nil
flag = false
#{old_each}(*a) { |e|
Thread.current.instance_eval {
@last_iteration=false
}
b.call element if flag
flag=true
element=e
}
if flag
Thread.current.instance_eval {
@last_iteration=true
}
b.call element
Thread.current.instance_eval {
@last_iteration=false
}
end
end
FINISH
end
end
class Array; include EachWithLast end
a = [1,2,3,4,5].select {|e| last? }
p a # => [5]
class Range; include EachWithLast end
class String; include EachWithLast end
for i in 1…10
print “and the last is: " if last?
print i
“ds\njkd\njsk\nsd”.each { |l| puts " - the last line is #{l}” if last? }
end
class File; include EachWithLast end
File.open(“oe.rb”) { |f|
count = 0
f.each { |line|
count += line.length
print “last line: #{line}” if last?
}
print “total chars: #{count}\n”
} #THE LAST LINE
Also, “if prev yield prev” fails if there’s a nil or a false in the
array. That’s why you need (afaics) an explicit flag to say ‘this is the
first iteration; prev has no value’. I tried leaving prev undefined and
rescuing NameError instead, but then the value doesn’t persist beyond
the block.
Is there any built in functionality for iteration that will allow me
to
···
On Fri, May 30, 2003 at 08:33:15PM +0900, Carlos wrote:
detect when I am on the last element?
…
Here is a module that implements it, but you should include it in every
class where you want #each to behave that way :-/.
…
def EachWithLast.append_features(klass)
old_each=“each_”+rand(10000).to_s+Time.now.usec.to_s
klass.class_eval <<-FINISH
Err??!
I must be missing something; why is this not an incredibly simple thing to
achieve?
module Enumerable
def each_with_last
prev = nil
each_with_index do |item,index|
yield prev,false if index > 0
prev = item
end
yield prev,true
end
end
irb(main):011:0> [1,2,3,4,5].each_with_last do |a,b| puts a,b; end
1
false
2
false
3
false
4
false
5
true
It was Nobu’s comment that this should work with IO which made me think it
must work this way: you simply keep reading items until there are no more
(eof in the case of IO), and then you yield with ‘true’ for the last one.
module Enumerable
def each_with_last
each_with_index {|e,i| yield(e, i == size-1)}
end
end
this won’t work for anything Enumerable though - though it works for Array
and Hash.
“The Enumerable mixin provides collection classes with several traversal and
searching methods, and with the ability to sort. The class must provide a
method each, which yields successive members of the collection. If
Enumerable#max , #min, or #sort is used, the objects in the collection must
also implement a meaningful <=> operator, as these methods rely on an ordering
between members of the collection.”
so you may or may not have a size method.
(Hmmm… could this be the non-superfluous use of Enumerable#each_with_index
(as opposed to just Array#ewi) that I’ve been looking for for 2.5 years?
Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
~ > ruby -e ‘p % ^) .intern’
====================================
OK, as penance for clouding the issue with the mythical
Enumerable#size, I feel morally obliged to write a real version
I’m not sure how horrible this is. Very, I suspect. But anyway,
another possibility:
module Enumerable
def each_with_last
n = -1
each { n += 1 }
each_with_index {|e,i| yield(e, i == n)}
end
end
Enumerable doesn’t guarantee multiple iterations. e.g. IO.
module Enumerable
def each_with_last
n = -1
each { n += 1 }
each_with_index {|e,i| yield(e, i == n)}
end
end
It benchmarks a bit faster than yours, for arrays of size 10 to 10000,
but not wildly so.
My flag version benchmarks fater still
user system total real
flag: 146.570000 0.270000 146.840000 (167.307243)
index: 190.220000 1.170000 191.390000 (219.334280)
Also, if we’re really bumming cycles (and depending on how differently
you want to treat the last element), you could yield 0…n-1 and return
n, saving the two-valued yield and the test in the client method.
The Enumerable mixin provides collection classes with several
traversal and searching methods, and with the ability to sort. The
class must provide a method each, which yields successive members of
the collection.
So they only rely on the existence of ‘each’, unless you are using
sort/min/max.
I thought the question was “how do I get a method like ‘each’, but which
includes a flag saying whether this is the last element or not?” In which
case, you just define another method which does that, called (say)
‘each_with_last’, which makes use of ‘each’. Where does this fail?
Cheers,
Brian.
···
On Fri, May 30, 2003 at 10:09:39PM +0900, Carlos wrote:
I must be missing something; why is this not an incredibly simple thing to
achieve?
module Enumerable
def each_with_last
The methods in Enumerable don’t call #each_with_last, they call #each… But now it seems that they don’t call #each either :-/.
Detecting if the first element might in some circumstances server the same
purpose and it’s easy done without Thread tricks:
def print_format(enum)
first = true
str = “[”
enum.each do |e|
if first
first = false
else
str << ", "
end
str << e.to_s
end
str << “]”
str
end
a=[10, 30, “foo”]
puts print_format(a)
robert
“Carlos” angus@quovadis.com.ar schrieb im Newsbeitrag
news:20030530113250.GB1403@quovadis.com.ar…
Is there any built in functionality for iteration that will allow me to
detect when I am on the last element? This would not be hard for an
Array,
using array.length, but what about a Hash? How would I know when I am
at
the last element?
Here is a module that implements it, but you should include it in every
class where you want #each to behave that way :-/. (And some #each-like
methods (for example, String#each_byte, don’t work at all…)
oe.rb:
#!/usr/bin/ruby
class Object
def last?
Thread.current.last_iteration?
end
private :last?
end
class Thread
def last_iteration?
@last_iteration
end
end
def each(*a,&b)
element = nil
flag = false
#{old_each}(*a) { |e|
Thread.current.instance_eval {
@last_iteration=false
}
b.call element if flag
flag=true
element=e
}
if flag
Thread.current.instance_eval {
@last_iteration=true
}
b.call element
Thread.current.instance_eval {
@last_iteration=false
}
end
end
FINISH
end
end
class Array; include EachWithLast end
a = [1,2,3,4,5].select {|e| last? }
p a # => [5]
class Range; include EachWithLast end
class String; include EachWithLast end
for i in 1…10
print “and the last is: " if last?
print i
“ds\njkd\njsk\nsd”.each { |l| puts " - the last line is #{l}” if last? }
end
class File; include EachWithLast end
File.open(“oe.rb”) { |f|
count = 0
f.each { |line|
count += line.length
print “last line: #{line}” if last?
}
print “total chars: #{count}\n”
} #THE LAST LINE
More precisely, it only works if the array (or other collection) elements
have a defined <=> method which can compare them.
irb(main):010:0> [1,5,3,2,1].max
=> 5
However that’s still no good, as it involves iterating over the collection a
second time. For something like an IO object, attached to a pipe which can’t
be rewound, you can’t iterative over it twice.
Regards,
Brian.
···
On Sat, May 31, 2003 at 03:13:16AM +0900, Anders Borch wrote:
John Johnson wrote:
I may be over simplifiying, but Enumerable defines max, so couldn’t you:
a = {}
a[“a”] = “A”
a[“b”] = “B”
a[“c”] = “C”
a.each { |key, val|
print “#{key} = #{val}”
if key === a.keys.max
puts " and that’s the last one"
else
puts
end
}
Oh OK - make ‘last?’ a sort of pseudo-instance variable rather than passing
it as a parameter to each_with_last. Looks messy, as it splits the iterator
state: some carried by the iterator, and some carried outside it.
Cheers,
Brian.
···
On Sat, May 31, 2003 at 12:09:00AM +0900, ts wrote:
Did someone propose implementing each_with_last in terms of select?
Oh OK - make 'last?' a sort of pseudo-instance variable rather than passing
it as a parameter to each_with_last. Looks messy, as it splits the iterator
state: some carried by the iterator, and some carried outside it.
But you don't solve the problem, in [ruby-talk:72510] you have
class Array; include EachWithLast end
a = [1,2,3,4,5].select {|e| last? }
p a # => [5]