chaniks / crystal

The Crystal Programming Language
http://crystal-lang.org
Other
0 stars 0 forks source link

non-copying iteration #4

Open chaniks opened 8 years ago

chaniks commented 8 years ago
class Indexable::ItemIterator
  def limit(from : Int , count : Int, &block)
    skip(from).first(count).each { |v| yield v } 
  end 

  def limit(range : Range, &block)
      take = range.end
      take += 1 unless range.excludes_end?
      first(take).skip(range.begin).each { |v| yield v } 
  end 
end

array = ["a", "b", "c", "d", "e"]

array[1,3].each { |x| print x, " -- " }
puts

array.each.limit(1,3) { |x| print x, " -- " } 
puts

array.each.limit(1..3) { |x| print x, " -- " }
puts

Popped up in my mind while reading crystal-lang's #3386

each for me means everything. So I split them up.

Though, I never liked the way something[1,3] where 3 implies the count, I'd better remove limit(1,3) but implement limit(n &block) instead:

array.each.skip(1).limit(3) { ... }

The naming is also a concern.

array.each.limit(1..3) { ... }
array.each.for(1..3) { ... }
array.each.within(1..3) { ... }
array.each.only(1..3) { ... }
array.each.run(1..3) { ... }

array.each.skip(1).run(3) { ... }
array.each.run(3, offset: 1)  { ... }
array.each.from(1).run(3) { ... } # (rename) skip -> from
array.each.from(1).limit(3) { ... }
array.each.run(3, from: 1) { ... }
chaniks commented 8 years ago

Gosh..

Indexable uses 3 custom iterators internally, without a common parent. Iterator itself is the only common module.

Iterator itself is not index dependent, and to support ranges with negative ends, I should change all those three iterators, or add a super class for them, which I guess too much for this small change. (or use Enumerable#size, which will iterate thru the end. Very undesirable.)

Let's check if there's any other way.

chaniks commented 8 years ago

Should Iterator have #limit(range), or only Indexable#each needs it? Will I use Indexable#reverse_each with #limit? Will I use Indexable#each_index with #limit?

Many questions. Headache.

chaniks commented 8 years ago

And also, if I implement Iterator#limit(range), and if we use it during an iteration, should the range begin from the current position?

For example:

iter = (1..10).each
iter.next
iter.next
iter.limit(1..3) { |i| puts i }  # => ???
chaniks commented 8 years ago

Hmm.

Doc says: (https://crystal-lang.org/api/0.19.3/Iterator.html#first%28n%3AInt%29-instance-method)

def first(n : Int) Returns an iterator that only returns the first n elements of the initial iterator.

But:

iter = (1..5).each
iter.next
iter = iter.first(3)
iter.next # => 2 (should be 3)
iter.next # => 3 (should be 4)
iter.next # => 4 (should be Iterator::Stop)
iter.next # => #<Iterator::Stop:...>
chaniks commented 8 years ago

okay. now it's getting even messier. maybe better to use current provided apis. Iterator and Indexable are already too heavy.

chaniks commented 8 years ago

Probably I want Iterator has only #next. Maybe because I'm from too old days.

And do something like:

Iterator.utilize(SomeIterableClass)

obj = SomeIterableClass.new 
obj.each.take_while { ... } 
# and so on...

Or

UtilizedIterator.wrap(obj.each).take_while(...)

if really needed for performance improvement.

Concerning performance already breaks readability..

Okay, I admit it is too Java.