Czy istnieje mechanizm pętli x razy w ES6 (ECMAScript 6) bez zmiennych mutowalnych?


157

Typowy sposób na zapętlenie xczasów w JavaScript to:

for (var i = 0; i < x; i++)
  doStuff(i);

Ale nie chcę używać ++operatora ani w ogóle mieć żadnych zmiennych zmiennych. Czy jest więc sposób w ES6 na zapętlenie xczasów w inny sposób? Uwielbiam mechanizm Ruby:

x.times do |i|
  do_stuff(i)
end

Coś podobnego w JavaScript / ES6? Mógłbym trochę oszukać i stworzyć własny generator:

function* times(x) {
  for (var i = 0; i < x; i++)
    yield i;
}

for (var i of times(5)) {
  console.log(i);
}

Oczywiście, że nadal używam i++. Przynajmniej jest poza zasięgiem wzroku :), ale mam nadzieję, że w ES6 jest lepszy mechanizm.


3
Dlaczego zmienna sterująca zmienną pętlą jest problemem? Tylko zasada?
doldt

1
@doldt - próbuję uczyć JavaScript, ale eksperymentuję z odkładaniem koncepcji zmiennych zmiennych na później
godz.

5
Wychodzimy z tematu, ale czy jesteś pewien, że przejście do generatorów ES6 (lub jakiejkolwiek innej nowej koncepcji wysokiego poziomu) jest dobrym pomysłem, zanim dowiedzą się o zmiennych zmiennych? :)
doldt

5
@doldt - może, eksperymentuję. Podejście funkcjonalnego języka do JavaScript.
godz.

Użyj let, aby zadeklarować tę zmienną w pętli. Jego zakres kończy się pętlą.
ncmathsadist

Odpowiedzi:


156

DOBRZE!

Poniższy kod jest napisany przy użyciu składni ES6, ale można go równie łatwo napisać w ES5 lub nawet mniej. ES6 nie jest wymagane, aby utworzyć „mechanizm pętli x razy”


Jeśli nie potrzebujesz iteratora w wywołaniu zwrotnym , jest to najprostsza implementacja

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'))

Jeśli potrzebujesz iteratora , możesz użyć nazwanej funkcji wewnętrznej z parametrem licznika, aby wykonać iterację

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'))


Przestań czytać tutaj, jeśli nie lubisz uczyć się więcej rzeczy ...

Ale coś powinno czuć w tych ...

  • ifInstrukcje pojedynczej gałęzi są brzydkie - co się dzieje w drugiej gałęzi?
  • wiele instrukcji / wyrażeń w ciałach funkcyjnych - czy obawy dotyczące procedury są mieszane?
  • niejawnie zwrócone undefined- wskazanie nieczystej funkcji powodującej skutki uboczne

"Czy nie ma lepszego sposobu?"

Jest. Przyjrzyjmy się najpierw naszej początkowej implementacji

// times :: Int -> (void -> void) -> void
const times = x => f => {
  if (x > 0) {
    f()               // has to be side-effecting function
    times (x - 1) (f)
  }
}

Jasne, to proste, ale zwróć uwagę, jak po prostu dzwonimy f()i nic z tym nie robimy. To naprawdę ogranicza rodzaj funkcji, którą możemy powtarzać wielokrotnie. Nawet jeśli mamy dostępny iterator, f(i)nie jest dużo bardziej wszechstronny.

A jeśli zaczniemy od lepszego rodzaju procedury powtarzania funkcji? Może coś, co lepiej wykorzystuje dane wejściowe i wyjściowe.

Powtarzanie funkcji ogólnej

// 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

Powyżej zdefiniowaliśmy funkcję ogólną, repeatktóra pobiera dodatkowe dane wejściowe, które są używane do rozpoczęcia wielokrotnego stosowania pojedynczej funkcji.

// 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)))

Wdrażanie timeszrepeat

Cóż, teraz jest to łatwe; prawie cała praca jest już wykonana.

// 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'))

Ponieważ nasza funkcja przyjmuje idane wejściowe i zwraca i + 1, działa to skutecznie jako nasz iterator, do którego przechodzimy za fkażdym razem.

Naprawiliśmy również naszą listę punktowaną problemów

  • Nigdy więcej brzydkich pojedynczych ifinstrukcji
  • Treści o pojedynczym wyrażeniu wskazują na ładnie rozdzielone problemy
  • Nigdy więcej bezużytecznych, pośrednio zwróconych undefined

Operator przecinka JavaScript,

Jeśli nie możesz zobaczyć, jak działa ostatni przykład, zależy to od twojej świadomości jednego z najstarszych toporów bojowych JavaScript; operator przecinek - krótko mówiąc, ocenia wyrażenia od lewej do prawej i zwraca wartość ostatniego wyrażenia ocenianej

(expr1 :: a, expr2 :: b, expr3 :: c) :: c

W naszym powyższym przykładzie używam

(i => (f(i), i + 1))

co jest tylko zwięzłym sposobem pisania

(i => { f(i); return i + 1 })

Tail Call Optimization

Choć rekurencyjne implementacje są seksowne, w tym momencie byłoby nieodpowiedzialne, gdybym je polecał, biorąc pod uwagę, że żadna maszyna wirtualna JavaScript , o której myślę, nie obsługuje prawidłowej eliminacji wywołań ogonowych - do transpozycji używano babel, ale jest w stanie "zepsuty; zostanie ponownie zaimplementowany status przez ponad rok.

repeat (1e6) (someFunc) (x)
// => RangeError: Maximum call stack size exceeded

W związku z tym powinniśmy ponownie przyjrzeć się naszej implementacji, repeataby była bezpieczna dla stosu.

Poniższy kod nie używać zmiennych zmienny ni xnależy jednak pamiętać, że wszystkie mutacje są zlokalizowane w repeatfunkcji - bez zmiany stanu (mutacje) są widoczne z zewnątrz funkcji

// 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

Wiele z was będzie mówić „ale to nie działa!” - Wiem, zrelaksuj się. Możemy zaimplementować interfejs loop/ styl Clojure recurdo pętli w przestrzeni stałej przy użyciu czystych wyrażeń ; nic z tego while.

Tutaj abstrahujemy whileod naszej loopfunkcji - szuka ona specjalnego recurtypu, aby utrzymać działanie pętli. Gdy recurnapotkany zostanie inny typ, pętla jest zakończona i zwracany jest wynik obliczenia

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


24
Wydaje się zbyt skomplikowane (jestem szczególnie zdezorientowany g => g(g)(x)). Czy istnieje korzyść z funkcji wyższego rzędu nad funkcją pierwszego rzędu, jak w moim rozwiązaniu?
Pavlo

1
@naomik: dzięki za poświęcenie czasu na opublikowanie linku. bardzo cenione.
Pineda

1
@ AlfonsoPérez Doceniam tę uwagę. Zobaczę, czy uda mi się gdzieś tam popracować ^ _ ^
Dziękuję

1
@naomik Farewell TCO ! Jestem zdruzgotany.

10
Wygląda na to, że ta odpowiedź jest akceptowana i dobrze oceniana, ponieważ musiała wymagać wiele wysiłku, ale nie sądzę, że jest to dobra odpowiedź. Prawidłowa odpowiedź na pytanie brzmi „nie”. Dobrze jest wymienić obejście, tak jak to zrobiłeś, ale zaraz potem stwierdzisz, że jest lepszy sposób. Dlaczego po prostu nie umieścisz tej odpowiedzi i nie usuniesz gorszej na górze? Dlaczego wyjaśniasz operatory przecinków? Dlaczego wychowujesz Clojure? Po co w ogóle tyle stycznych do pytania z dwuznakową odpowiedzią? Proste pytania to nie tylko platforma dla użytkowników do prezentacji pewnych ciekawych faktów dotyczących programowania.
Timofey 'Sasha' Kondrashov

266

Korzystanie z operatora Spread 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);

Lub jeśli nie potrzebujesz wyniku:

[...Array(10)].forEach((_, i) => {
  console.log(i);
});

// as a one liner
[...Array(10)].forEach((_, i) => console.log(i));

Lub używając operatora 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);

Zauważ, że jeśli potrzebujesz tylko powtórzenia ciągu, możesz użyć String.prototype.repeat .

console.log("0".repeat(10))
// 0000000000

26
Lepiej:Array.from(Array(10), (_, i) => i*10)
Bergi

6
To powinna być najlepsza odpowiedź. Więc ES6! Świetnie!
Gergely Fehérvári

3
Jeśli nie potrzebujesz iteratora (i), możesz wykluczyć zarówno klucz, jak i wartość, aby to zrobić:[...Array(10)].forEach(() => console.log('looping 10 times');
Sterling Bourne

9
Więc przydzielasz całą tablicę elementów N tylko po to, aby ją wyrzucić?
Kugel

2
Czy ktoś odniósł się do poprzedniego komentarza Kugla? Zastanawiałem się nad tym samym
Arman

37
for (let i of Array(100).keys()) {
    console.log(i)
}

To działa, więc to świetnie! Ale jest trochę brzydki w tym sensie, że potrzebna jest dodatkowa praca, a nie do tego Arraysłużą klucze.
godz.

@w. w rzeczy samej. Ale nie jestem pewien, czy istnieje synonim haskella [0..x]w JS, który jest bardziej zwięzły niż w mojej odpowiedzi.
zerkmy

możesz mieć rację, że nie ma nic bardziej zwięzłego niż to.
godz.

OK, rozumiem, dlaczego to działa, biorąc pod uwagę różnice między Array.prototype.keysi Object.prototype.keys, ale na pierwszy rzut oka jest to mylące.
Mark Reed

1
@cchamberlain z TCO w ES2015 (choć nigdzie nie zaimplementowany?) może być mniej niepokojący, ale rzeczywiście :-)
zerkms

29

Myślę, że najlepszym rozwiązaniem jest użycie let:

for (let i=0; i<100; i++) 

Spowoduje to utworzenie nowej ( izmiennej ) zmiennej dla każdej oceny treści i zapewni, że izostanie ona zmieniona tylko w wyrażeniu inkrementacji w tej składni pętli, a nie z innego miejsca.

Mógłbym trochę oszukiwać i stworzyć własny generator. Przynajmniej i++jest poza zasięgiem wzroku :)

To powinno wystarczyć imo. Nawet w czystych językach wszystkie operacje (lub przynajmniej ich interpretery) są zbudowane z prymitywów, które używają mutacji. Dopóki ma odpowiedni zakres, nie widzę, co w tym złego.

Powinieneś być w porządku

function* times(n) {
  for (let i = 0; i < x; i++)
    yield i;
}
for (const i of times(5))
  console.log(i);

Ale nie chcę używać ++operatora ani w ogóle mieć żadnych zmiennych zmiennych.

Wtedy jedynym wyborem jest użycie rekurencji. Możesz zdefiniować tę funkcję generatora również bez mutacji i:

function* range(i, n) {
  if (i >= n) return;
  yield i;
  return yield* range(i+1, n);
}
times = (n) => range(0, n);

Ale wydaje mi się to przesada i może powodować problemy z wydajnością (ponieważ eliminacja ogonów nie jest dostępna return yield*).


1
Podoba mi się ta opcja - ładna i prosta!
DanV

2
Jest to proste i na temat i nie powoduje alokacji tablicy, jak wiele odpowiedzi powyżej
Kugel

@Kugel Drugi może jednak przydzielić na stosie
Bergi

Słuszna uwaga, nie jestem pewien, czy optymalizacja wywołań ogonowych zadziała tutaj @Bergi
Kugel,



11

Odpowiedź: 9 grudnia 2015

Osobiście uważam, że zaakceptowana odpowiedź jest zarówno zwięzła (dobra), jak i zwięzła (zła). Doceń, że to stwierdzenie może być subiektywne, więc przeczytaj tę odpowiedź i sprawdź, czy się zgadzasz, czy nie

Przykład podany w pytaniu był podobny do Rubiego:

x.times do |i|
  do_stuff(i)
end

Wyrażenie tego w JS za pomocą poniższych pozwoliłoby na:

times(x)(doStuff(i));

Oto kod:

let times = (n) => {
  return (f) => {
    Array(n).fill().map((_, i) => f(i));
  };
};

Otóż ​​to!

Prosty przykład użycia:

let cheer = () => console.log('Hip hip hooray!');

times(3)(cheer);

//Hip hip hooray!
//Hip hip hooray!
//Hip hip hooray!

Alternatywnie, postępując zgodnie z przykładami zaakceptowanej odpowiedzi:

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'

Uwaga dodatkowa - Definiowanie funkcji zakresu

Podobnym / pokrewnym pytaniem, które wykorzystuje zasadniczo bardzo podobne konstrukcje kodu, może być wygodna funkcja Range w (rdzeniu) JavaScript, coś podobnego do funkcji zakresu podkreślenia.

Utwórz tablicę z n liczbami, zaczynając od x

Podkreślać

_.range(x, x + n)

ES2015

Kilka alternatyw:

Array(n).fill().map((_, i) => x + i)

Array.from(Array(n), (_, i) => x + i)

Demo przy użyciu 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 ]

W krótkim teście, który przeprowadziłem, z każdym z powyższych uruchomionych milion razy przy użyciu naszego rozwiązania i funkcji doStuff, poprzednie podejście (Array (n) .fill ()) okazało się nieco szybsze.


8
Array(100).fill().map((_,i)=> console.log(i) );

Ta wersja spełnia wymóg niezmienności PO. Rozważ także użycie reducezamiastmap zależności od przypadku użycia.

Jest to również opcja, jeśli nie przeszkadza ci mała mutacja w twoim prototypie.

Number.prototype.times = function(f) {
   return Array(this.valueOf()).fill().map((_,i)=>f(i));
};

Teraz możemy to zrobić

((3).times(i=>console.log(i)));

+1 do arcseldona za .fillsugestię.


Odebrano , ponieważ metoda wypełnienia nie jest obsługiwana w IE, Opera ani PhantomJS
morhook

8

Oto kolejna dobra alternatywa:

Array.from({ length: 3}).map(...);

Najlepiej, jak zauważył @Dave Morse w komentarzach, możesz również pozbyć się mapwywołania, używając drugiego parametru Array.fromfunkcji, takiego jak:

Array.from({ length: 3 }, () => (...))


2
To powinna być akceptowana odpowiedź! Jedna mała sugestia - otrzymujesz już funkcjonalność podobną do mapy, której potrzebujesz za darmo dzięki Array.from: Array.from({ length: label.length }, (_, i) => (...)) Oszczędza to tworzenie pustej tymczasowej tablicy tylko po to, aby rozpocząć wywołanie mapy.
Dave Morse

7

Nie jest to coś, czego bym uczył (lub kiedykolwiek używałbym w moim kodzie), ale oto rozwiązanie godne Codegolf bez mutowania zmiennej, bez potrzeby ES6:

Array.apply(null, {length: 10}).forEach(function(_, i){
    doStuff(i);
})

Bardziej interesujący dowód słuszności koncepcji niż użyteczna odpowiedź.


Nie Array.apply(null, {length: 10})można być sprawiedliwym Array(10)?
Pavlo

1
@Pavlo, właściwie nie. Array (10) utworzyłby tablicę o długości 10, ale bez zdefiniowanych w niej kluczy, co powoduje, że konstrukcja forEach nie jest użyteczna w tym przypadku. Ale rzeczywiście można to uprościć, jeśli nie używasz forEach, zobacz odpowiedź Zerkms (chociaż używa ES6!).
doldt

kreatywny @doldt, ale szukam czegoś łatwego do nauczenia i prostego.
godz.

5

Spóźniłem się na imprezę, ale ponieważ to pytanie często pojawia się w wynikach wyszukiwania, chciałbym tylko dodać rozwiązanie, które uważam za najlepsze pod względem czytelności, a jednocześnie nie jest długie (które jest idealne dla każdej bazy kodów IMO) . To mutuje, ale dokonałbym tego kompromisu dla zasad KISS.

let times = 5
while( times-- )
    console.log(times)
// logs 4, 3, 2, 1, 0

3
Dziękuję, że jesteś głosem rozsądku w czymś, co mogę opisać tylko jako imprezę fetyszową wyższego rzędu. Ja również skończyłem na tym pytaniu i odpowiedzi, podążając za nieszkodliwym pierwszym trafieniem w Google i szybko zostałem zbezczeszczony przez większość odpowiedzi tutaj. Twój jest pierwszym na liście, który uznałbym za proste rozwiązanie prostego problemu.
Martin Devillers

Jedynym problemem jest to, że jest to trochę sprzeczne z intuicją, jeśli chcesz użyć timeszmiennej wewnątrz pętli. Być może countdownbyłoby lepsze nazewnictwo. W przeciwnym razie najczystsza i najjaśniejsza odpowiedź na stronie.
Tony Brasunas

3

Ahaik, w ES6 nie ma mechanizmu podobnego do timesmetody Rubiego . Ale możesz uniknąć mutacji, używając rekurencji:

let times = (i, cb, l = i) => {
  if (i === 0) return;

  cb(l - i);
  times(i - 1, cb, l);
}

times(5, i => doStuff(i));

Demo: http://jsbin.com/koyecovano/1/edit?js,console


Podoba mi się to podejście, uwielbiam rekurencję. Ale chciałbym coś prostszego, aby pokazać nowe pętle użytkowników JavaScript.
godz.


2

W paradygmacie funkcjonalnym repeatjest zwykle nieskończoną funkcją rekurencyjną. Aby go użyć, potrzebujemy leniwej oceny lub stylu zaliczania kontynuacji.

Leniwy oceniał powtarzanie funkcji

const repeat = f => x => [x, () => repeat(f) (f(x))];
const take = n => ([x, f]) => n === 0 ? x : take(n - 1) (f());

console.log(
  take(8) (repeat(x => x * 2) (1)) // 256
);

Używam thunk (funkcji bez argumentów), aby osiągnąć leniwą ocenę w JavaScript.

Powtarzanie funkcji ze stylem przekazywania kontynuacji

const repeat = f => x => [x, k => k(repeat(f) (f(x)))];
const take = n => ([x, k]) => n === 0 ? x : k(take(n - 1));

console.log(
  take(8) (repeat(x => x * 2) (1)) // 256
);

CPS jest na początku trochę przerażający. Jednak zawsze według takiego samego schematu: Ostatni argument jest kontynuacją (funkcja), która powołuje się na swój własny podmiot: k => k(...). Należy pamiętać, że CPS wywraca aplikację na lewą stronę, tj. take(8) (repeat...)Staje się miejscem, w k(take(8)) (...)którym kjest częściowo zastosowanarepeat .

Wniosek

Oddzielając powtórzenie ( repeat) od warunku zakończenia ( take) uzyskujemy elastyczność - rozdzielenie spraw aż do ich gorzkiego końca: D


1

Zalety tego rozwiązania

  • Najprostszy do odczytania / użycia (imo)
  • Wartość zwracana może być użyta jako suma lub po prostu zignorowana
  • Zwykła wersja es6, również link do wersji kodu w języku TypeScript

Wady - mutacja. Będąc tylko wewnętrznym, nie obchodzi mnie to, może inni też nie.

Przykłady i kod

times(5, 3)                       // 15    (3+3+3+3+3)

times(5, (i) => Math.pow(2,i) )   // 31    (1+2+4+8+16)

times(5, '<br/>')                 // <br/><br/><br/><br/><br/>

times(3, (i, count) => {          // name[0], name[1], name[2]
    let n = 'name[' + i + ']'
    if (i < count-1)
        n += ', '
    return n
})

function times(count, callbackOrScalar) {
    let type = typeof callbackOrScalar
    let sum
    if (type === 'number') sum = 0
    else if (type === 'string') sum = ''

    for (let j = 0; j < count; j++) {
        if (type === 'function') {
            const callback = callbackOrScalar
            const result = callback(j, count)
            if (typeof result === 'number' || typeof result === 'string')
                sum = sum === undefined ? result : sum + result
        }
        else if (type === 'number' || type === 'string') {
            const scalar = callbackOrScalar
            sum = sum === undefined ? scalar : sum + scalar
        }
    }
    return sum
}

Wersja TypeScipt
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011


0

zajęcie się aspektem funkcjonalnym:

function times(n, f) {
    var _f = function (f) {
        var i;
        for (i = 0; i < n; i++) {
            f(i);
        }
    };
    return typeof f === 'function' && _f(f) || _f;
}
times(6)(function (v) {
    console.log('in parts: ' + v);
});
times(6, function (v) {
    console.log('complete: ' + v);
});

5
„zajęcie się aspektem funkcjonalnym”, a następnie użycie imperatywnej pętli ze zmienną i. Jaki jest powód, aby używać nawet timeszwykłego starego for?
zerkmy

ponowne użycie jak var twice = times(2);.
Nina Scholz

Dlaczego więc nie użyć po prostu fordwa razy?
zerkmy

nie boję się używać do. chodziło o to, żeby nie używać variabele. ale rezultatem jest zawsze jakiś rodzaj buforowania, czyli zmienna.
Nina Scholz

1
„było czymś, czego nie można używać variabele” - i nadal go używasz - i++. Nie jest oczywiste, jak zawijanie czegoś niedopuszczalnego w funkcji sprawia, że ​​jest ona lepsza.
zerkmy

0

Generatory? Rekursja? Dlaczego tak bardzo nienawidzę mutacji? ;-)

Jeśli jest to dopuszczalne tak długo jak my „ukryć”, a potem po prostu zaakceptować zastosowanie operatora jednoargumentowego i możemy zachować rzeczy proste :

Number.prototype.times = function(f) { let n=0 ; while(this.valueOf() > n) f(n++) }

Tak jak w rubinie:

> (3).times(console.log)
0
1
2

2
Kciuki w górę: „Dlaczego tak bardzo nienawidzisz mutacji?”
Sarreph

1
Kciuki w górę za prostotę, kciuki w dół za trochę za dużo rubinowego stylu z małpą. Po prostu powiedz nie tym złym, złym małpom.
mrm

1
@mrm to „małpa łatanie”, czy to nie tylko przypadek rozszerzenia? Objąć i rozciągnąć :)
conny

Nie. Dodawanie funkcji do Number (lub String, Array lub jakiejkolwiek innej klasy, której nie stworzyłeś) jest z definicji albo polifillami, albo małpimi łatami - a nawet polyfillami nie są zalecane. Przeczytaj definicje „małpiej łaty”, „wypełnienia polyfill” i zalecanej alternatywy, „wypełnienia kucyka”. To jest to czego chcesz.
mrm

Aby rozszerzyć liczbę, należy: class SuperNumber extends Number {times (fn) {for (let i = 0; i <this; i ++) {fn (i); }}}
Alexander,

0

Odpowiedź @Tieme s opakowałem funkcją pomocniczą.

W języku TypeScript:

export const mapN = <T = any[]>(count: number, fn: (...args: any[]) => T): T[] => [...Array(count)].map((_, i) => fn())

Teraz możesz biegać:

const arr: string[] = mapN(3, () => 'something')
// returns ['something', 'something', 'something']

0

Ja to zrobiłem:

function repeat(func, times) {
    for (var i=0; i<times; i++) {
        func(i);
    }
}

Stosowanie:

repeat(function(i) {
    console.log("Hello, World! - "+i);
}, 5)

/*
Returns:
Hello, World! - 0
Hello, World! - 1
Hello, World! - 2
Hello, World! - 3
Hello, World! - 4
*/

iZmienna zwraca ilość razy to zapętlone - przydatna, gdy trzeba by wstępnie kwotę X obrazów.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.