The subsum example is a bit like the programs that interest me, but in a miniature. I more often deal with trees and graphs and search algorithms, and less often arrays of floating point numbers.
On Sunday, August 5, 2012 9:52:07 AM UTC-4, Patrick O'Leary wrote:
Like MATLAB, the special index end represents the last index:
first, others = items[1], items[2:end]
Thanks! It looks like 'end' will be very useful. In this case the initial index '2' still does cause the error.
However, this might be quicker, using a dequeue method from Base:
first = shift(items)
With the use of shift, the function gradually removes elements from the items array. The items array is passed in to two recursive calls of subsum. Unfortunately the first call chews up the array before the second call can use it. One fix for this problem is to make a copy of the array before it is passed in to the first recursive call (so it can chew harmlessly on that) and the second call can work on the original. The version below uses 'shift' and it works, but it also does an extra copy:
function subsum(total, items)
if total == 0
{[]}
elseif items == []
{}
else
first = shift(items)
if first > total
subsum(total, items)
else
[subsum(total, copy(items)),
insert_each(first, subsum(total-first, items))]
end
end
end
The purely functional version is easier to reason about, and safer in multi-threaded programs. My style is not at all pure, but I try for a functional style as much as I can without being too wasteful.
That's a MATLAB carryover, but more efficiently, again using the dequeue methods:
enqueue(items, item)
This also changes each one of the items arrays rather than creating new ones. In subsum it would not not harmful, but I was being cautious. The function might not be as useful for other applications if it alters the original elements of its input.
Besides that, I expect adding to and splitting from the end of the array is more efficient than from the beginning.
This would certainly be true if I did not make copies. Its is also interesting to compare this version written in Scala:
type IntVectors = Vector[Vector[Int]]
def subsum(total: Int, items: Vector[Int]): IntVectors = {
if (total == 0)
Vector(Vector())
else if (items.isEmpty)
Vector()
else {
val (first, others) = (items.head, items.tail)
if (first > total)
subsum(total, others)
else
subsum(total, others) ++
(subsum(total - first, others) map (first +: _))
}
}
println(subsum(10, Vector(1, 2, 3, 4, 5, 6, 7, 8, 9)))
I intensionally used Vectors (immutable 1-d arrays) instead of Scala's cons-lists. This version does something interesting: it creates tiny 'display' objects that represent slices (pointers of sorts) to the original Vector Because displays are passed into the recursive calls of subsum, there is no need to make copies of the original integer Vectors. 'head' and 'tail' operations just involve adjusting an index in the display. Scala also creates objects that represent concatenations of display slices, so the result is actually (efficiently) assembled only at the end of the process.
Laziness sometimes pays off.
Hope this helps!
It was very helpful. Thanks!