Стан, що змінюється, і петлі. Ви майже ніколи не потребуєте їх, і ви майже завжди отримуєте кращий код без них.
Наприклад, це взято безпосередньо з потоку 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, він майже прямо говорить «згрупуйте речі за типом»), і набагато менше схильний до помилок. Немає небезпеки вибігати з кінця масиву, помилок стовпчиків або помилок одноосібно з індексами циклу та умовами завершення, оскільки немає індексів циклу та умов припинення.