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 번 반복하는 메커니즘”을 만들 필요 는 없습니다
콜백에서 반복자가 필요하지 않은 경우 가장 간단한 구현입니다.
const times = x => f => {
if (x > 0) {
f()
times (x - 1) (f)
}
}
// use it
times (3) (() => console.log('hi'))
// or define intermediate functions for reuse
let twice = times (2)
// twice the power !
twice (() => console.log('double vision'))
반복자가 필요한 경우 카운터 매개 변수와 함께 명명 된 내부 함수를 사용하여 반복 할 수 있습니다.
const times = n => f => {
let iter = i => {
if (i === n) return
f (i)
iter (i + 1)
}
return iter (0)
}
times (3) (i => console.log(i, 'hi'))
더 많은 것을 배우고 싶지 않다면 여기서 읽지 마십시오 …
그러나 그에 대해 뭔가를 느끼게해야합니다 …
- 단일 브랜치
if
문은 추악 합니다. 다른 브랜치에서는 어떻게됩니까? - 함수 본문의 여러 문장 / 표현 — 절차 문제가 혼합되어 있습니까?
- 암시 적으로 반환
undefined
-불완전한 부작용의 표시
“더 나은 방법이 없습니까?”
있습니다. 먼저 초기 구현을 다시 살펴 보겠습니다
// times :: Int -> (void -> void) -> void
const times = x => f => {
if (x > 0) {
f() // has to be side-effecting function
times (x - 1) (f)
}
}
물론 간단하지만 우리가 어떻게 전화를 걸고 f()
아무 것도하지 않는 것을 주목 하십시오. 이것은 실제로 여러 번 반복 할 수있는 기능의 유형을 제한합니다. 반복자를 사용할 수 있다고해도 f(i)
훨씬 다재다능하지는 않습니다.
더 나은 종류의 함수 반복 절차로 시작한다면 어떨까요? 아마도 입력과 출력을 더 잘 사용하는 것입니다.
일반 함수 반복
// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
if (n > 0)
return repeat (n - 1) (f) (f (x))
else
return x
}
// power :: Int -> Int -> Int
const power = base => exp => {
// repeat <exp> times, <base> * <x>, starting with 1
return repeat (exp) (x => base * x) (1)
}
console.log(power (2) (8))
// => 256
위에서 우리 repeat
는 단일 함수의 반복 적용을 시작하는 데 사용되는 추가 입력을 취하는 일반 함수를 정의했습니다 .
// repeat 3 times, the function f, starting with x ...
var result = repeat (3) (f) (x)
// is the same as ...
var result = f(f(f(x)))
구현 times
과repeat
지금 이것은 쉬운 일입니다. 거의 모든 작업이 이미 완료되었습니다.
// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
if (n > 0)
return repeat (n - 1) (f) (f (x))
else
return x
}
// times :: Int -> (Int -> Int) -> Int
const times = n=> f=>
repeat (n) (i => (f(i), i + 1)) (0)
// use it
times (3) (i => console.log(i, 'hi'))
함수는 i
입력으로 사용하고를 반환 i + 1
하기 때문에 f
매번 전달하는 반복자로 효과적으로 작동합니다 .
총알 문제 목록도 수정했습니다.
- 더 이상 못생긴 단일 지점
if
진술 - 단일 표현 체는 멋지게 분리 된 우려를 나타냅니다
- 더 이상 쓸모없고 암시 적으로 반환되지 않음
undefined
자바 스크립트 쉼표 연산자
마지막 예제가 어떻게 작동하는지 보는 데 어려움을 겪고 있다면 JavaScript의 가장 오래된 전투 축 중 하나에 대한 인식에 달려 있습니다. 쉼표 연산자 – 짧은에, 그것은 왼쪽에서 오른쪽으로 식을 평가하고 반환 마지막 평가 식의 값을
(expr1 :: a, expr2 :: b, expr3 :: c) :: c
위의 예에서 저는
(i => (f(i), i + 1))
간결한 글쓰기 방식입니다
(i => { f(i); return i + 1 })
테일 콜 최적화
재귀 적 구현만큼 섹시하지만이 시점에서 자바 스크립트 VM이 적절한 꼬리 호출 제거를 지원할 수 있다고 생각할 수 없기 때문에 권장하는 것은 무책임 할 것입니다. “1 년 이상 지위.
repeat (1e6) (someFunc) (x)
// => RangeError: Maximum call stack size exceeded
따라서 우리는 우리의 구현을 다시 방문해야합니다 repeat
스택 안전을 하기 위해 합니다.
아래의 코드는 않습니다 가변 변수를 사용하지 않는 n
및 x
모든 돌연변이가에 국한되어 있지만, 참고 repeat
어떠한 상태 변화 (돌연변이) 함수의 외부에서 볼 수 있습니다을 – 기능을
// repeat :: Int -> (a -> a) -> (a -> a)
const repeat = n => f => x =>
{
let m = 0, acc = x
while (m < n)
(m = m + 1, acc = f (acc))
return acc
}
// inc :: Int -> Int
const inc = x =>
x + 1
console.log (repeat (1e8) (inc) (0))
// 100000000
이것은 “하지만 기능하지 않습니다!”라고 말하는 많은 사람들을 갖게 될 것입니다. – 알아, 진정해. 순수 표현식을 사용하여 상수 공간 반복을위한 Clojure 스타일 loop
/ recur
인터페이스를 구현할 수 있습니다 . 그중 아무것도 없습니다 .while
여기에서 우리는 추상적 인 while
우리와 멀리 loop
기능 – 그것은 특별한 찾습니다 recur
루프 실행을 유지하는 유형입니다. recur
유형이 아닌 경우 루프가 완료되고 계산 결과가 반환됩니다.
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const repeat = $n => f => x =>
loop ((n = $n, acc = x) =>
n === 0
? acc
: recur (n - 1, f (acc)))
const inc = x =>
x + 1
const fibonacci = $n =>
loop ((n = $n, a = 0, b = 1) =>
n === 0
? a
: recur (n - 1, b, a + b))
console.log (repeat (1e7) (inc) (0)) // 10000000
console.log (fibonacci (100)) // 354224848179262000000
답변
은 Using ES2015 확산 연산자를 :
[...Array(n)].map()
const res = [...Array(10)].map((_, i) => {
return i * 10;
});
// as a one liner
const res = [...Array(10)].map((_, i) => i * 10);
또는 결과가 필요하지 않은 경우 :
[...Array(10)].forEach((_, i) => {
console.log(i);
});
// as a one liner
[...Array(10)].forEach((_, i) => console.log(i));
또는 ES2015 Array.from 연산자를 사용하십시오 .
Array.from(...)
const res = Array.from(Array(10)).map((_, i) => {
return i * 10;
});
// as a one liner
const res = Array.from(Array(10)).map((_, i) => i * 10);
반복되는 문자열이 필요한 경우 String.prototype.repeat 를 사용할 수 있습니다 .
console.log("0".repeat(10))
// 0000000000
답변
for (let i of Array(100).keys()) {
console.log(i)
}
답변
최선의 해결책은 다음을 사용하는 것입니다 let
.
for (let i=0; i<100; i++) …
그러면 i
각 바디 평가에 대해 새로운 (변경 가능) 변수 가 생성되고 i
다른 곳이 아닌 해당 루프 구문의 증분 식에서 만 변경됩니다.
속임수를 쓰고 제 자신의 발전기를 만들 수있었습니다. 적어도
i++
시야가 맞지 않습니다 🙂
충분할 것입니다. 순수한 언어로도 모든 연산 (또는 적어도 그 해석기)은 돌연변이를 사용하는 프리미티브로 만들어집니다. 범위가 올바르게 지정되어 있으면 문제가 무엇인지 알 수 없습니다.
당신은 잘해야합니다
function* times(n) {
for (let i = 0; i < x; i++)
yield i;
}
for (const i of times(5))
console.log(i);
그러나
++
연산자 를 사용 하거나 변경 가능한 변수를 원하지 않습니다 .
그런 다음 유일한 선택은 재귀를 사용하는 것입니다. 가변성없이 생성기 함수를 정의 할 수도 i
있습니다.
function* range(i, n) {
if (i >= n) return;
yield i;
return yield* range(i+1, n);
}
times = (n) => range(0, n);
그러나 그것은 저에게 과도하게 보이고 성능 문제가있을 수 있습니다 (꼬리 제거 기능을 사용할 수 없으므로 return yield*
).
답변
const times = 4;
new Array(times).fill().map(() => console.log('test'));
이 스 니펫은 console.log
test
4 번입니다.
답변
나는 그것이 매우 간단하다고 생각합니다.
[...Array(3).keys()]
또는
Array(3).fill()
답변
답변 : 2015 년 12 월 9 일
개인적으로 나는 간결한 답변과 간결한 답변을 찾았습니다. 이 진술이 주관적 일 수 있음을 이해하므로이 답변을 읽고 동의하거나 동의하지 않는지 확인하십시오.
질문에 주어진 예는 Ruby와 같은 것입니다.
x.times do |i|
do_stuff(i)
end
아래를 사용하여 JS에서 이것을 표현하면 허용됩니다.
times(x)(doStuff(i));
코드는 다음과 같습니다.
let times = (n) => {
return (f) => {
Array(n).fill().map((_, i) => f(i));
};
};
그게 다야!
간단한 사용 예 :
let cheer = () => console.log('Hip hip hooray!');
times(3)(cheer);
//Hip hip hooray!
//Hip hip hooray!
//Hip hip hooray!
또는 허용되는 답변의 예를 따르십시오.
let doStuff = (i) => console.log(i, ' hi'),
once = times(1),
twice = times(2),
thrice = times(3);
once(doStuff);
//0 ' hi'
twice(doStuff);
//0 ' hi'
//1 ' hi'
thrice(doStuff);
//0 ' hi'
//1 ' hi'
//2 ' hi'
참고-범위 기능 정의
근본적으로 매우 유사한 코드 구성을 사용하는 유사 / 관련 질문은 밑줄의 범위 함수와 비슷한 (핵심) JavaScript에 편리한 범위 함수가있을 수 있습니다.
x부터 시작하여 n 개의 숫자로 배열을 만듭니다.
밑줄
_.range(x, x + n)
ES2015
몇 가지 대안 :
Array(n).fill().map((_, i) => x + i)
Array.from(Array(n), (_, i) => x + i)
n = 10, x = 1을 사용한 데모
> Array(10).fill().map((_, i) => i + 1)
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
> Array.from(Array(10), (_, i) => i + 1)
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
빠른 테스트에서 위의 각 솔루션과 doStuff 함수를 사용하여 위의 각 백만 번 실행하여 이전 접근법 (Array (n) .fill ())이 약간 더 빨랐습니다.