
Replacing Loops in Swift - tambourine_man
http://blog.wilshipley.com/2015/08/pimp-my-code-book-2-eliminating-loops.html
======
jeffreyrogers
Over the past few years there's been a tendency towards a more functional
style of programming, like what's demonstrated in this blog post. I think
there are a lot of benefits to this, but the functional style doesn't hold a
monopoly on good code.

One of the advantages of the "slightly better", more imperative, method of
finding a 3D piece under the mouse over the "beautiful", more functional, one
is that it is extremely clear what the code is doing, while the functional
case needs some thought to understand; even for someone who knows what map,
filter, etc. do!

This isn't to say that functional is bad and imperative is good, or really to
choose a style at all. But sometimes the cleanest idea mathematically isn't
the best way to write code.

~~~
jlarocco
One pitfall I often fall into with map, filter, etc. is that I chain them
together, forgetting each of them takes another pass through the sequence.
With an explicit loop, it's often easier to reduce multiple passes into a
single pass.

~~~
dragonwriter
> One pitfall I often fall into with map, filter, etc. is that I chain them
> together, forgetting each of them takes another pass through the sequence.

Lazy maps, filters, etc. that do not do this are available in many languages
(and the default in some.)

Transducers provide a similar benefit, from a somewhat opposite direction
(composing reduction functions rather than chaining lazy iterating functions).

Other than familiarity for some people, I don't see a real edge to imperative
iteration over functional operations here.

~~~
jlarocco
I guess I should have been more clear.

I'm aware that they do not add extra passes in some languages.

But in Swift, C++, Common Lisp, OCaml, Python, Ruby, Perl, Java, Scheme, and
many others, adding another map/filter/etc. will add another pass through the
sequence unless you're going out of the way to avoid it.

~~~
ta0967
how much of a performance drop have you measured in real world? compare

    
    
        my @judges = ();
        for my $sport (@sports) {
          if (is_watersport $sport) {
            push @judges, get_judges $sport;
          }
        }
    

to

    
    
        my @judges = map get_judges, grep is_watersport, @sports;
    

is it worth fretting over an added pass? how many sports are we dealing with
anyway? and IME, nested loops induce errors in the form of _accidental_
`O(N*M)`.

~~~
jlarocco
Your example is a straw man.

IRL, I've seen this crop up while processing database results and large
datasets. On several occasions I've shaved up to several minutes off of
execution times by replacing chained map, filter, and zip calls that were
iterating through the data multiple times with more complicated expressions
that only performed the iteration once.

I've also written a graphics applications where converting several chained
map/filters into a single pass made interacting with the application
noticeably less laggy.

I'm actually surprised so many people have challenged this as a potential
pitfall. I didn't pull it out of the air or from trivial examples. I've seen
it happen.

------
dep_b
Some comments on this article: first of all it's not about the amount of lines
of code that attribute to the complexity of the code, but the amount of things
that actually happen and can fail in your code that can make it fragile. A
godegolfed line of code is not more reliable than everything neatly expanded
and tabbed in lines.

Frequently in C# LINQ-queries turn out to be very long indiscernible chains of
things happening that aren't completely obvious in what they're doing. Same
goes for filter / map / forEach. I use them a lot but I put them often in
several variables ('let' in this case of course) just to have the "one thing
happening in one line" kind of code that's so much nicer to the eyes.

I never benchmarked it, but I never had to. I assume modern compilers that see
a constant that is only being used once understand that it comes down to the
same as chaining directly.

The best takeaway from this story is that filter / map / etcetera better show
the _intent_ of the code. That's why I love the `guard` statement so much. It
doesn't do anything I couldn't do before but it simply tells the other
programmer "unless this passes nothing else below this should happen".

~~~
kwhitefoot
LINQ queries are horrible. I tend to rewrite them using the extension methods
instead, it usually makes them a lot clearer.

------
VeejayRampay
It's nice to note when you think about for loops, while, etc. versus
map/each/reduce that the latter offer a convenience that is not discussed
enough: the ability to reason about a single element of a collection without
having ever to think about the collection itself (no thinking about bounds,
indices or things like that).

This in turn, makes it easier to think in terms of :

I have a thing, I put it in the box and a new different thing comes out of
that box. That in turn can more easily lead to thinking about series of
connected boxes.

~~~
Sharlin
And after that, you can start thinking about other kinds of boxes:
Maybe/Optional, Either/Try, futures, infinite collections, lazy collections,
I/O streams, message passing systems... all of which can be manipulated with
the same set of primitives. And at some point you realize there's a name for
this fancy abstraction and it starts with an "m" and ends with a "d" and
Haskellers like it very much.

------
vvanders
One downside of this approach is that you don't have as much discrete control
over your access patterns.

In his intersection case he's having to hit at least some items twice,
depending his cache locality that could be pretty painful.

That said I do map/filter is great for areas that aren't performance critical.

------
lispm
Even though he does not like LISP, welcome to the 1960s with map, remove, ...

Those who learned SICP in school or university will have no problem to
recognize the concepts.

------
jimbokun
"What first struck me when learning Swift was how much play the "map()" and
"filter()" functions got on the web [also "flatmap()" but I'm not as excited
about that one yet because I don't love LISP and it's not 1970]."

Cute. But this article demonstrates Eric Raymond's point:

"LISP is worth learning for a different reason — the profound enlightenment
experience you will have when you finally get it."

Much of the history of main stream programming languages is simply a slow
progression of adopting the features of Lisp. So in this case, someone who
already knew Lisp would not have to learn how to use the functional constructs
in Swift fluently and idiomatically, because they would already have
experience doing so.

(Yes, I know this does not apply to all programming languages and programming
language features, but it's surprising how often it's true! Paul's Graham take
on this idea here:
[http://www.paulgraham.com/diff.html](http://www.paulgraham.com/diff.html))

------
tel
When you're standing here you're in for a real treat once flatmap starts
making sense and making its way into your hear^H^H^H^H code.

~~~
jlarocco
I don't understand why so many people are so enthusiastic about flatmap. It's
never seemed particularly difficult to understand, and in my experience is
less useful than map.

The Scala community in particular seems to have a comical number of articles
praising it and explaining how to use it. I just don't get it.

~~~
dllthomas
Flatmap is "bind" from Haskell - a part of the interface called "Monad", about
which many people are excited for both good reasons and poor reasons.

------
an4rchy
Wow.. didn't know Objective C has been around for that long (32 Years), this
guy has been programming in it for 26 yrs..

