* Posts by Dave Burt

3 posts • joined 1 Sep 2006

To iterate is human

Dave Burt

Three or four... but which ones?

Thanks, Kevlin, for publishing a second article on why my response to "Up with cohesion, down with coupling" was wrong!

(Readers, you can see the first response article here: http://www.regdeveloper.co.uk/2006/09/04/to_iterate_human/)

The Iterator does not give the caller any valuable information on the position of elements, so the constraint "if the caller needs to be able to know the position of elements in some way" does not in fact differentiate between internal and external iterators. In fact, the word "position" implies order, and external iterators apply just as well to unordered collections like your sets and your hash-maps. (Of course, because iterating over elements is what most collections are for.) Yes, an Iterator does encapsulate position, but preferably doesn't expose that implementation-specific detail, for compatibility with different kinds of collection.

Again, you downplay Lisp's fold functions for providing more than just the element to the loop function, but these functions demonstrate how "position of the elements" and other context as required by task constraints can naturally be included in your external iterator. Thus your constraint allows internal iterators while excluding Java's preferred Iterators.

Furthermore, I put it to you again that while iteration over a collection is a very common idiom, this constraint is very rarely necessary. That's why using for-each makes sense in any language that provides such syntax.

I think I adequately explained the benefits of external iterators in my response to your first article -- chiefly, they encapsulate the most common function of a collection class within the class, and don't expose those implentation-specific details to the users of the class.

Hayden Clark, as dumb as he claims to be, has shown how simple it is to bring the pattern to C. You don't need "closures, generics, an object-based type system or other suitable mechanisms." And (void *)'s are cool, anyway. All it says is "I don't feel then need to nail down a type here."

0
0

To iterate is human

Dave Burt

To enumerate, divine

I haven't been a regular Java user since 1.4, so it slipped my mind that, indeed, Java has specific syntax sugar for the Iterator. It didn't slip my mind that an internal iterator call would look frightful:

col.foreach(new Callable() { public void call(Object obj) {

// loop code goes here

}})

Verity Stob demonstrates in "Out of the (C++) loop", language idiom does (and should) depend on what's legible in the syntax of the language.

If Java 1.6 had closures, as you say, that would open the door for such things. (Is it just me, or is Java getting less nerfed as it goes along?)

I still dislike the idea of having two classes that are so completely interdependent as a Java Collection/Iterator pair. I also find an Iterator's usually (not most classes, most loops -- arrays/lists) an abstraction that adds complexity for no benefit.

And, as I pointed out earlier, 90% of the times you want to iterate over a loop, you do just want to run a bit of code against each object in the collection. The Enumeration pattern is the obvious way to do that without the caller having to worry about the details.

You mention Smalltalk; I believe that's where Ruby gets its iterator style from. And you mention Lisp's map inaccurately. Lisps have other functions that apply a function to each element of a list, notably foldl and family.

0
0

Up with cohesion, down with coupling

Dave Burt

Internal Iterator

External iterator classes are verbose to use and to define. You need to define an iterator class for each collection class, and you need to update them in parallel. To use it, you need to instantiate the iterator and make method calls to iterate, fetch and see if there are more.

In 99% of use cases, all you want to do is run a block of code against each element.

A fourth option, which is in the general case superior to all three of your options, is the internal iterator. It delegates management of the iteration to the collection, rather than adding repetitive boilerplate to your functional code.

An internal iterator is an instance method of a collection which is passed a function which it calls for each element in the collection.

An example of internal iterators is seen in Ruby's core collection classes.

For the technically-minded, let me demonstrate the difference between the two with code (Ruby, because it's shorter). (Download the code from http://dave.burt.id.au/ruby/pair.rb to see it indented properly.)

class Pair

attr_accessor :first, :last

def initialize(first = nil, last = nil)

@first = first

@last = last

end

# The internal iterator is an instance method

def each

yield first

yield last

end

# The external iterator is a full-blown, separate class

# which also wants an instance method to obtain an

# iterator instance

def iterator

Iterator.new(self)

end

# Wow, this iterator's definition is longer than the

# collection's!

class Iterator

def initialize(pair)

@pair = pair

@next = :first

end

def has_next?

@next.nil?

end

def next

case @next

when :first

@next = :last

first

when :last

@next = nil

last

else

fail

end

end

end

end

col = Pair.new "a", "b"

# Internal iterator: just pass the block to the iterator

col.each do |x|

print x

end

# External iterator: instantiate, check, fetch

iter = col.iterator

while iter.has_next?

x = iter.next

print x

end

0
0

Forums