Типичный способ зацикливания x
времени в JavaScript:
for (var i = 0; i < x; i++)
doStuff(i);
Но я не хочу использовать ++
оператор или иметь какие-либо изменяемые переменные вообще. Так есть ли в ES6 способ зациклить x
время другим способом? Я люблю механизм Руби:
x.times do |i|
do_stuff(i)
end
Что-нибудь похожее в JavaScript / ES6? Я мог бы обмануть и сделать свой собственный генератор:
function* times(x) {
for (var i = 0; i < x; i++)
yield i;
}
for (var i of times(5)) {
console.log(i);
}
Конечно я все еще использую i++
. По крайней мере, это вне поля зрения :), но я надеюсь, что в ES6 есть лучший механизм.
Ответы:
ХОРОШО!
Приведенный ниже код написан с использованием синтаксиса ES6, но с таким же успехом может быть написан на ES5 или даже меньше. ES6 не является обязательным требованием для создания «механизма зацикливания на x разах »
Если вам не нужен итератор в обратном вызове , это самая простая реализация
Если вам нужен итератор , вы можете использовать именованную внутреннюю функцию с параметром счетчика для итерации за вас
Но что-то должно чувствовать себя не так в этих ...
if
операторы ветвления уродливы - что происходит в другой ветке?undefined
- указание на нечистую побочную функцию"Разве нет лучшего способа?"
Там есть. Давайте сначала вернемся к нашей первоначальной реализации
Конечно, все просто, но обратите внимание, как мы просто звоним
f()
и ничего с этим не делаем. Это действительно ограничивает тип функции, которую мы можем повторять несколько раз. Даже если у нас есть итератор,f(i)
он не намного более универсален.Что если мы начнем с лучшего вида процедуры повторения функций? Может быть, то, что лучше использовать ввод и вывод.
Повторение общей функции
Выше мы определили обобщенную
repeat
функцию, которая принимает дополнительный ввод, который используется для запуска повторного применения одной функции.Реализация
times
сrepeat
Ну, теперь это легко; почти вся работа уже сделана.
Поскольку наша функция принимает
i
в качестве входных данных и возвращаетi + 1
, это эффективно работает как наш итератор, который мы передаемf
каждый раз.Мы также исправили наш список проблем
if
операторовundefined
Оператор запятой JavaScript,
В случае, если вам сложно увидеть, как работает последний пример, это зависит от вашей осведомленности об одной из самых старых боевых осей JavaScript; оператор запятой - короче говоря, он вычисляет выражения слева направо и возвращает значение последнего вычисленного выражения
В нашем примере выше, я использую
это просто лаконичный способ написания
Оптимизация вызовов
Как бы ни были сексуальны рекурсивные реализации, в этот момент я бы безответственно порекомендовал их, учитывая, что никакая виртуальная машина JavaScript, о которой я могу думать, не поддерживает правильное устранение хвостовых вызовов - babel использовал ее для переноса, но он был «сломан», будет переопределено статус более года.
repeat
Поэтому мы должны пересмотреть нашу реализацию, чтобы сделать ее безопасной для стека.Приведенный ниже код делает не использовать изменяемые переменные
n
иx
не отметить , что все мутации локализованы вrepeat
функцию - нет изменений состояния (мутация) видна снаружи функцииЭто заставит многих говорить "но это не функционально!" - Я знаю, просто расслабься. Мы можем реализовать Clojure-стиль
loop
/recur
интерфейс для зацикливания в постоянном пространстве, используя чистые выражения ; ничего такогоwhile
.Здесь мы абстрагируемся
while
от нашейloop
функции - она ищет специальныйrecur
тип для поддержания цикла в рабочем состоянии. Когда встречается неrecur
тип, цикл завершается, и возвращается результат вычисленияисточник
g => g(g)(x)
). Есть ли польза от функции более высокого порядка по сравнению с функцией первого порядка, как в моем решении?Использование оператора ES2015 Spread :
[...Array(n)].map()
Или, если вам не нужен результат:
Или с помощью оператора ES2015 Array.from :
Array.from(...)
Обратите внимание, что если вам просто нужно повторить строку, вы можете использовать String.prototype.repeat .
источник
Array.from(Array(10), (_, i) => i*10)
[...Array(10)].forEach(() => console.log('looping 10 times');
источник
Array
, для чего используются ключи.[0..x]
в JS синоним haskell более лаконичен, чем в моем ответе.Array.prototype.keys
иObject.prototype.keys
, но это, на первый взгляд, сбивает с толку.Я думаю, что лучшим решением является использование
let
:Это создаст новую (изменяемую)
i
переменную для каждой оценки тела и гарантирует, чтоi
она изменяется только в выражении приращения в синтаксисе этого цикла, а не откуда-либо еще.Это должно быть достаточно IMO. Даже на чистых языках все операции (или, по крайней мере, их интерпретаторы) построены из примитивов, которые используют мутации. Пока это правильно определено, я не вижу, что с этим не так.
Вы должны быть в порядке с
Тогда ваш единственный выбор - использовать рекурсию. Вы можете определить эту функцию генератора также без изменяемого
i
:Но это кажется мне излишним и может иметь проблемы с производительностью (так как устранение хвостовых вызовов недоступно
return yield*
).источник
Этот фрагмент будет
console.log
test
4 раза.источник
Я думаю, что это довольно просто:
или
источник
Ответ: 09 декабря 2015
Лично я нашел принятый ответ как кратким (хорошо), так и кратко (плохо). Оценить это утверждение может быть субъективным, поэтому, пожалуйста, прочитайте этот ответ и посмотрите, согласны вы или не согласны
Пример, приведенный в вопросе, был похож на пример Руби:
Выражение этого в JS с использованием ниже позволит:
Вот код:
Это оно!
Простой пример использования:
В качестве альтернативы, следуя примерам принятого ответа:
Примечание: определение функции диапазона
Подобный / связанный вопрос, который использует принципиально очень похожие конструкции кода, может заключаться в том, есть ли удобная функция Range в (основном) JavaScript, что-то похожее на функцию диапазона подчеркивания.
Создать массив из n чисел, начиная с x
Нижнее подчеркивание
ES2015
Пара альтернатив:
Демонстрация с использованием n = 10, x = 1:
В быстром тесте, который я выполнил, каждый из которых выполнялся миллион раз, каждый из которых использовал наше решение и функцию doStuff, первый подход (Array (n) .fill ()) оказался немного быстрее.
источник
Эта версия удовлетворяет требованию OP о неизменности. Также рассмотрите возможность использования
reduce
вместоmap
зависимости от вашего варианта использования.Это также вариант, если вы не возражаете против небольшой мутации в вашем прототипе.
Теперь мы можем сделать это
+1 к arcseldon за
.fill
предложение.источник
Вот еще одна хорошая альтернатива:
Предпочтительно, как @Dave Morse указал в комментариях, вы также можете избавиться от
map
вызова, используя второй параметрArray.from
функции, например так:источник
Array.from
на MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Array.from({ length: label.length }, (_, i) => (...))
это экономит создание пустого временного массива только для запуска вызова карты.Не то, чему я бы научил (или когда-либо использовал в своем коде), но вот решение, достойное кода, без изменения переменной, нет необходимости в ES6:
На самом деле это скорее интересная проверка концепции, чем полезный ответ.
источник
Array.apply(null, {length: 10})
может быть простоArray(10)
?Я опаздываю на вечеринку, но так как этот вопрос часто появляется в результатах поиска, я просто хотел бы добавить решение, которое я считаю лучшим с точки зрения читабельности, но не длинное (которое идеально подходит для любой кодовой базы IMO) , Он мутирует, но я бы сделал компромисс для принципов KISS.
источник
times
переменную внутри цикла. Возможно,countdown
будет лучше назвать. В противном случае самый чистый и понятный ответ на странице.Афаик, в ES6 нет механизма, подобного
times
методу Руби . Но вы можете избежать мутации, используя рекурсию:Демо: http://jsbin.com/koyecovano/1/edit?js,console
источник
Если вы хотите использовать библиотеку, есть также знак
_.times
или подчеркивание_.times
:Обратите внимание, что это возвращает массив результатов, так что это действительно больше похоже на этот ruby:
источник
В функциональной парадигме
repeat
обычно есть бесконечная рекурсивная функция. Чтобы использовать его, нам нужен либо ленивый анализ, либо стиль прохождения продолжения.Ленивый оценил функцию повторения
Я использую thunk (функцию без аргументов) для достижения ленивых вычислений в Javascript.
Повторение функции со стилем прохождения продолжения
CPS поначалу немного пугает. Однако, всегда следует по той же схеме: Последний аргумент является продолжением (функция), которая вызывает его собственное тело:
k => k(...)
. Обратите внимание, что CPS выворачивает приложение наизнанку, то естьtake(8) (repeat...)
становитсяk(take(8)) (...)
там, гдеk
применяется частичноrepeat
.Вывод
Отделяя repetition (
repeat
) от условия завершения (take
), мы получаем гибкость - разделение проблем до самого конца: Dисточник
Преимущества этого решения
Недостатки - мутация. Быть внутренним только мне все равно, может быть, некоторые другие тоже не будут.
Примеры и код
Версия TypeScipt
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011
источник
решение функционального аспекта:
источник
i
. Какова причина, чтобы даже использоватьtimes
поверх старогоfor
?var twice = times(2);
.for
дважды?i++
. Не очевидно, как обертывание чего-либо недопустимого в функции делает его лучше.Генераторы? Рекурсия? Почему так много ненавидят? ;-)
Если это приемлемо, пока мы его «скрываем», тогда просто примите использование унарного оператора, и мы можем упростить ситуацию :
Прямо как в рубине
источник
Я обернул ответ @Tieme с помощью вспомогательной функции.
В TypeScript:
Теперь вы можете запустить:
источник
Я сделал это:
Использование:
i
Переменные возвращает количество раз она петельное - полезно , если вам нужно для предварительной загрузки х количества изображений.источник