That's a great point. Often in my programming career, I've used the rule of least power[1]. In the looping/flow control ladder, I'd list descending
* JMP/GOTO
* for/while
* map/filter/reduce
If a problem only needs a map, using a for introduces needless complexity(ie: state). Personally, I think we have a pedagogical problem in that we teach people loops first and people confuse what they've been taught first with what is a reasonable choice[2].
For completeness, recursion has it's own ladder
* PUSH,POP,JMP
* function call
* recursion schemes[3]
Side note: recursion schemes have an enormous hurdle to adoption in that mathematicians got there first and the naming are so off putting that no one will take you serious if you bring up even simple schemes like anamorphism and catamorphism. Folks would rather accept a FactoryFactoryFactory over a futumorphism.
1. https://en.wikipedia.org/wiki/Rule_of_least_power
2. The other side of this coin is, "Why would they teach me a bad way of doing something?"
The claim is that by expressing programs in terms of these array primitives, you (a) can have those primitives be super optimised and (b) better see what the program is actually about without worrying too much about eg what names to give all the loop counters, all the other intermediate state associated with iteration, etc.
(dotimes (i 10)
(print i))
is just a predefined macro that might expand into primitives like (BLOCK NIL
(LET ((I 0))
(TAGBODY
#:LOOP-2867
(IF (>= I 10)
(GO #:END-2868))
(PRINT I)
(PSETQ I (1+ I))
(GO #:LOOP-2867)
#:END-2868
(RETURN-FROM NIL (PROGN NIL)))))Using higher-order functions instead of loops is similar e.g. loops:
# input_ = "1,2,3"
numbers = (int(d) for d in input_.split(","))
product = 1
for n in numbers:
product *= n
can be replaced: numbers = map(int, input_.split(","))
product = reduce(mul, numbers, 1)But it's a different paradigm than procedural programming and embracing it might lead to a different mind set.