Изменчивое состояние и циклы. Вы почти никогда не нуждаетесь в них, и вы почти всегда получаете лучший код без них.
Например, это берется непосредственно из потока 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
Они оба делают одно и то же. Но я понятия не имею, что они делают. К счастью, вопрос на самом деле объясняет, что они делают, поэтому я смог переписать их следующим образом:
// 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);
Там нет петель и нет изменяемого состояния. Ну, ладно, никаких явных циклов и счетчиков циклов.
Код стал намного короче, намного проще, намного больше похож на описание того, что код должен делать (особенно в случае с Ruby, он почти прямо говорит «группировать вещи по типу»), и гораздо менее подвержен ошибкам. Нет опасности убежать от конца массива, ошибок столба или ошибок «один за другим» с индексами цикла и условиями завершения, потому что нет никаких индексов цикла и условий завершения.