|
|
|
|
|
by tangus
225 days ago
|
|
It may be somewhat awkward; zipping collections to iterate over them in unison makes the first one (the sender) seem special, while it not necessarily is. And accumulating more than one value isn't always straightforward; you usually have to reduce explicitly. But it's so much better in the general case! Like the example you showcase your macro with. In Ruby it would be: lst = [[1, 2], :dud, [3, 4], [5, 6]]
lst.grep(Array).flatten.sum
=> 21
You can see what it does at a glance, you don't have to immerse yourself inside the directives to follow the logic. And when you can't do that, declaring iterators and accumulators outside a loop and then iterating and accumulating "by hand" yields code not so different than your more complex examples. def partition (arr)
yes = []
no = []
arr.each do |elt|
if yield elt
yes << elt
else
no << elt
end
end
[yes, no]
end
|
|
I would also reach for higher order functions for most things, but when you need things to be fast that is not how you would do things. You would express it with nested for loops and ifs, and you would have to juggle state all by yourself. That is my main critique of looping constructs: they only cover the absolute simplest case, and then you are by yourself.
The partition example is not a particularly nice implementation of partition. It is there to show that you can write non-tail recursive loops, meaning you can use it to do tree walking and such. Using mutation, like your example, would make it prettier, but would make the code unsafe with schemes call/cc. Even a traditional named let (tail recursion) would be prettier.