Pull to refresh

Comments 20

for (var j = 0; j < data[i].temperatures.length; j++) {
  totalTemperature += data[i].temperatures[j];
}

Неужели так ещё пишет кто-либо, закончивший школу?
<irony>++j же! Не могут эти джаваскриптеры в оптимизацию.</irony>
Если быть честным, то я не понял, как коррелируют между собой
для своих самых требовательных к производительности компонентов

и хотя бы даже
при функциональном подходе данные должны быть неизменны… создаёте новый массив с новыми значениями вместо изменения старого массива

а так же использование рекурсии вместо цикла (стек не бесконечный!), возврат функции из функции без использования прототипов (поправьте меня знатоки JS, если я неправ, у меня сильное подозрение, что функция getItem будет возвращать новый объект функции на каждом вызове).
В общем лично у меня сложилось такое впечатление, что это еще одна крайность, как и ООП головного мозга, когда за точное следование парадигме приходится платить!

Большинство компиляторов функциональных языков разворачивают хвостовую рекурсию в цикл
Вот только не JavaScript, пока что.
Даже не пока, а по стандарту.
TCO трудно совместима с полем arguments у функции-объекта, которое должно ссылаться на текущий инстанс аргументов.
А не могли бы вы развернуть мысль? Я не очень понимаю, как поле arguments мешает tco.
function f1(a) { f2() }
function f2() {
 console.print(f1.arguments)
}


Если компилятор делает TCO, то когда работает f2 вызванная из f1 фрейм стека f1 должет уже быть разрушен, а f1.arguments на него ссылается.
Сохранять f1.arguments без связи с фреймом тоже нельзя. Во первых не понятно, когда его можно разрушить, во вторых одновременно могут быть несколько фреймов f1 (если она будет рекурсивно вызываться) и f1.arguments должен перескакивать на старый при каждом завершении f1.
Спасибо за объяснение!
Автор сказал ведь что жс не функциональный язык и использовал его он лишь чтобы показать как все делается в функциональных ЯП.

Благодаря иммутабельности и еще некоторым фишкам на функциональных языках гораздо проще писать параллельный код.
Корреляция между неизменяемым состоянием и производительностью очень проста: код, не хранящий состояние, легко можно распараллелить, что очевидно может решить проблемы производительности. В случае работы только на одном ядре процессора это, конечно, ничего не ускорит.

Про рекурсию и конечность стека в статье говорится, как и про tail call optimization, которая при определенных условиях позволяет конечностью стека пренебречь.

getItem будет возвращать новую функцию каждый раз, когда она вызвана, да. Но вызывается в примере она только один раз, для того чтоб вернуть одну функцию, возвращающую значение ключа «temperature».
Самое забавное, что оптимизация хвостовой рекурсии как раз и работает за счёт отказа от неизменяемости состояния)
Так что у вас взаимоисключающие параграфы.
Интересно, а как вы связали эти мои два утверждения? Если я скажу, что зимой холодно, а в Африке тепло — вы тоже это во взаимоисключающие параграфы запишете?
Совсем не обязательно, если стек вызовов к состоянию не относить.
Можно отказаться от выделенного непрерывного стека и размещать фреймы вызовов в куче. При хвостовом вызове создается новый фрейм, а на старый теряется ссылка и его подбирает GC.
Такой подход, кроме простоты TCO, еще дает эффективные нити (был проект Stackless Python). И позволяет эффективно реализовать call-with-current-continuation, которого в js, с его любовью к ассинхронным API с каллбеками, очень не хватает.
В компилируемых языках достаточно хорошо совместимо. Компилятор все равно на каждое присваивание создает новую переменную в промежуточном коде, а потом уже распределяет их по регистрам/памяти с учетом времени жизни. С иммутабельными структурами данных все несколько хуже. Некоторые алгоритмы работают на них медленнее. Но за то иммутабельность позволяет разделять (share) данные между несколькими структурами — например при добавлении в список элемента старый список копировать не приходится.
А хвостовые вызовы многие компиляторы хорошо умеют оптимизировать. На gcc у меня они компилировались даже эффективнее, чем эквивалентный список.

Со временем это может и в джаваскрипт проникнуть, привыкать надо уже сейчас :-).
Благодарю за перевод. Первая часть статьи вполне доступно объяснила мне функциональный подход. Теперь можно двигаться дальше в изучении!
Scheme, конечно, функциональный ЯП, но SICP по большей части не о ФП, а о программировании в целом.
Рекурсия делает код более удобочитаемым

Далеко не всегда. Порой проще в цикле разобраться, чем проследить что за параметры придут в новом рекурсивном вызове.
Sign up to leave a comment.

Articles