Zmienny stan i pętle. Prawie nigdy ich nie potrzebujesz i prawie zawsze dostajesz lepszy kod bez nich.
Na przykład jest to pobierane bezpośrednio z wątku StackOverflow:
// ECMAScript
var thing, things_by_type = {};
for (var i = 0; i < things.length; i++) {
thing = things[i];
if(things_by_type[thing.type]) {
things_by_type[thing.type].push(thing);
} else {
things_by_type[thing.type] = [thing];
}
}
# Ruby
things_by_type = {}
things.each do |thing|
(things_by_type[thing.type] ||= []) << thing
end
Oboje robią to samo. Ale nie mam pojęcia, co oni robią. Na szczęście pytanie faktycznie wyjaśnia, co robią, więc mogłem je przepisać w następujący sposób:
// ECMAScript
things.reduce(function (acc, thing) {
(acc[thing.type] || (acc[thing.type] = [])).push(thing);
return acc;
}, {});
# Ruby
things.group_by(&:type)
// Scala
things groupBy(_.type)
// C#
from thing in things group thing by thing.Type // or
things.GroupBy(thing => thing.Type);
Nie ma pętli i nie można zmieniać stanu. No dobrze, bez wyraźnych pętli i liczników pętli.
Kod stał się znacznie krótszy, o wiele prostszy, bardziej podobny do opisu tego, co powinien robić (szczególnie w przypadku Rubiego, prawie bezpośrednio mówi „pogrupuj rzeczy według typu”) i znacznie mniej podatny na błędy. Nie ma niebezpieczeństwa ucieczki z końca tablicy, błędów słupka ogrodzeniowego lub błędów pojedynczych z indeksami pętli i warunkami zakończenia, ponieważ nie ma wskaźników pętli i warunków zakończenia.