John Hinsley jhinsley@telinco.co.uk wrote:
thirdArray := (1 to: firstArray size) collect: [:index | (firstArray at: index) + (secondArray at: index)]
Thanks Jon
I'm coming rapidly to the realisation that I find it very difficult to internalise iterations (and not only in Smalltalk!).
I wonder how common this is?
I've heard that in intro programming classes, loops are the place where most students fall out. if-then is fine, variables are okay, operations are okay, and even (I think) subroutines are okay, but loops really kill students off. This matches my scant experience, as well, teaching an occasional person how to program.
Loops are actually quite similar to recursive functions. This sounds strange, but consider. The theoretical description of the meaning of a loop is recursive -- do one loop, then do the test, then possibly recur. People who study small programs a lot recommend studying loops in terms of invariants plus movement towards a terminating condition -- pretty much the same as for recursive functions! In fact, not only are loops pretty similar to recursive functions, I think they may even be harder: at least with the recursive function, the function name can help you figure out the equivalent to an invariant ("gee, it says sorted(x,1,n) -- guess the result will be x with elements 1..n sorted"). With loops, you must reverse engineer the invariant.
Finally, I'll add my personal experience: I still find it tricky to add a *new* kind of loop to my code. Now, I can whip out a "iterate over the elements and accumulate into a subsidiary array" very quickly, but that's because I've seen this kind of loop a zillion times! If the loop has a structure that's new, I really have to think about it for a minute or two, even after 15-20 years of programming (depending on how you count). It just so turns out that almost all loops are familiar ones, so this isn't an issue for me in practice. Perhaps other programmers will introspect the same thing?
Anyway, now for the good news: Smalltalk allows you to get away from low-level loop algorithms in almost all cases that occur in practice. The reason Smalltlak can do this, and many languages can't, is because of the powerful combination of collections and blocks. Consider this wonderful response to the original question:
firstArray with: secondArray collect: [ :a :b | a + b ]
Is this really a loop? Technically, with:collect: is using a loop internally, but cognitively, it just doesn't seem the same to me as a hand-coded loop. All the details of incrementing an index and moving toward completion are left out, and it just looks like combining two collections with an operation. Smushing two collections together really seems simpler to me than "start with element 1; do stuff with element i; move to element i+1;" etc.
By the way, there is a nice generalization of the above with:foo: pattern. It looks like this:
(firstArray with: secondArray) collect: [ :combo | combo first + combo second ]
This use of #with: (which doesn't currently exist), is identical to ML and Haskel's "zip" function, and it allows you to use all of collect:, select:, reject:, do:, detect:, etc. without writing with:collect:, with:select:, with:reject:, etc. In brief, the presence of a "with" clause can be orthogonal to which collection operation is being used.
Overall, I'm really starting to think loops should be treated like assembly language or two's complement: interesting for the terminally curious, probably useful to know a *little* about, but not incredibly useful in most programs. (Of course, this is only true in a nice language that actually has collections and blocks!)
John, you're not at all alone. :)
-Lex