Değişken değişkenler olmadan ES6'da (ECMAScript 6) x kez döngü mekanizması var mı?


157

xJavaScript'te süreleri döngü yapmanın tipik yolu :

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

Ama ben ++operatörü kullanmak istemiyorum ya da hiç değişken değişkenim yok. Öyleyse ES6'da xzamanları başka bir yolla birleştirmenin bir yolu var mı? Ruby'nin mekanizmasını seviyorum:

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

JavaScript / ES6'da benzer bir şey var mı? Bir tür hile yapabilir ve kendi jeneratörümü yapabilirim:

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

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

Tabii ki hala kullanıyorum i++. En azından görünmez :), ancak ES6'da daha iyi bir mekanizma olmasını umuyorum.


3
Değişken döngü kontrol değişkeni neden bir sorundur? Sadece bir prensip mi?
doldt

1
@doldt - Ben JavaScript öğretmeye çalışıyorum ama sonrasına kadar değişken değişken kavramını geciktirerek ile deniyorum
at.

5
Burada gerçekten konu dışı oluyoruz, ancak ES6 jeneratörlerine (veya başka herhangi bir yeni, yüksek seviyeli konsepte)
geçmenin

5
@doldt - belki deniyorum. JavaScript'e işlevsel bir dil yaklaşımı uygulamak.
at.

Döngüdeki bu değişkeni bildirmek için let kullanın. Kapsamı döngü ile biter.
ncmathsadist

Yanıtlar:


156

TAMAM!

Aşağıdaki kod ES6 sözdizimleri kullanılarak yazılmıştır, ancak ES5 veya daha azıyla da kolayca yazılabilir. ES6, "x kez döngü mekanizması" oluşturmak için bir gereklilik değildir


Geri aramada yineleyiciye ihtiyacınız yoksa , bu en basit uygulamadır

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

Yineleyiciye ihtiyacınız varsa , sizin için yinelemek için bir counter parametresi ile adlandırılmış bir iç işlev kullanabilirsiniz

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


Daha fazla şey öğrenmek istemiyorsanız, burada okumayı bırakın ...

Ama bunlar hakkında bir şeyler hissetmeli ...

  • tek dal ififadeleri çirkin - diğer dalda ne olur?
  • işlev gövdelerinde birden çok ifade / ifade - prosedür kaygıları karışık mı?
  • dolaylı olarak iade undefined- safsızlık, yan etki fonksiyonu göstergesi

"Daha iyi bir yol yok mu?"

Var. Önce ilk uygulamamızı tekrar gözden geçirelim

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

Tabii, basit, ama nasıl aradığımızı f()ve onunla hiçbir şey yapmadığımızı fark et. Bu, birçok kez tekrarlayabileceğimiz fonksiyon türünü gerçekten sınırlar. Yineleyicimiz olsa bile, f(i)çok daha fazla yönlü değildir.

Daha iyi bir işlev tekrarlama prosedürü ile başlarsak ne olur? Belki de girdi ve çıktıyı daha iyi kullanan bir şey.

Genel fonksiyon tekrarı

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

Yukarıda, repeattek bir işlevin tekrarlanan uygulamasını başlatmak için kullanılan ek bir girdi alan genel bir işlev tanımladık .

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

Uygulama timesilerepeat

Şimdi bu kolay; işin neredeyse tamamı zaten yapılmış.

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

İşlevimiz igirdi i + 1olarak alındığından ve geri döndüğünden , bu fher seferinde geçirdiğimiz yineleyici olarak etkili bir şekilde çalışır .

Madde işaretleri listemizi de düzelttik

  • Artık çirkin tek dal ififadesi yok
  • Tek ifade gövdeleri hoş bir şekilde ayrılmış endişeleri gösterir
  • Artık işe yaramaz, dolaylı olarak iade edilmez undefined

JavaScript virgül operatörü,

Son örneğin nasıl çalıştığını görmekte sorun yaşıyorsanız, bu JavaScript'in en eski savaş eksenlerinden birine dair farkındalığınıza bağlıdır; virgül operatörü - Kısacası, soldan sağa ifadeleri değerlendirir ve döndürür son değerlendirilen ifadenin değerini

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

Yukarıdaki örneğimizde,

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

ki bu sadece kısa ve öz bir yazma şeklidir

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

Kuyruk Çağrısı Optimizasyonu

Özyinelemeli uygulamalar kadar seksi, bu noktada düşünebileceğim hiçbir JavaScript VM'sinin uygun kuyruk çağrısı eliminasyonunu desteklemediği düşünüldüğünde onları tavsiye etmem sorumsuz olurdu - babel onu aktarmak için kullanılır, ancak "kırıldı; yeniden uygulanacak "bir yıldan uzun süredir statü.

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

Bu nedenle, repeatyığını güvenli hale getirmek için uygulamamızı tekrar gözden geçirmeliyiz .

Aşağıdaki kod yapar değişken değişkenler kullanmak nve xtüm mutasyonlar lokalize verildiğini ancak not repeathiçbir devlet değişiklikleri (mutasyon) işlevi dışından görülebilir - fonksiyonu

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

Bu, birçoğunuzun "ama bu işlevsel değil!" - Biliyorum, sadece rahatla. Saf ifadeler kullanarak sabit boşluk döngüsü için bir Clojure stili loop/ recurarayüzü uygulayabiliriz ; bunların hiçbiri .while

Burada soyut whilebizim ile deplasmanda loopfonksiyonu - bu özel arar recurdöngü çalışır durumda tutmak için tip. Bir türle recurkarşılaşılmadığında döngü tamamlanır ve hesaplamanın sonucu döndürülür

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
Aşırı karmaşık görünüyor (özellikle kafam karıştı g => g(g)(x)). Çözümümdeki gibi birinci dereceden bir işleve göre daha üst düzey bir işlevden fayda var mı?
Pavlo

1
@ naomik: Bir bağlantı yayınlamak için zaman ayırdığınız için teşekkür ederiz. çok takdir etmek.
Pineda

1
@ AlfonsoPérez Açıklamayı takdir ediyorum. Orada bir yerde küçük bir ipucu çalışıp çalışamayacağımı göreceğim ^ _ ^
Teşekkür ederim

1
@naomik Elveda TCO ! Harap oldum.

10
Bu cevap kabul edilmiş ve iyi değerlendirilmiş gibi görünüyor çünkü çok çaba harcamalıydı, ama bunun iyi bir cevap olduğunu düşünmüyorum. Sorunun doğru cevabı "hayır" dır. Bir geçici çözümü listelediğiniz gibi listelemek yararlıdır, ancak bundan hemen sonra daha iyi bir yol olduğunu belirtirsiniz. Neden bu cevabı koyup en kötü olanı en üste çıkarmıyorsunuz? Neden virgül operatörlerini açıklıyorsunuz? Neden Clojure'u yetiştiriyorsun? Neden genel olarak 2 karakterli bir soru için bu kadar çok teğet? Basit sorular, kullanıcıların bazı düzgün programlama gerçekleri hakkında bir sunum yapmaları için bir platform değildir.
Timofey 'Sasha' Kondrashov

266

ES2015 Spread operatörünü kullanma :

[...Array(n)].map()

const res = [...Array(10)].map((_, i) => {
  return i * 10;
});

// as a one liner
const res = [...Array(10)].map((_, i) => i * 10);

Ya da sonuca ihtiyacınız yoksa:

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

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

Veya ES2015 Array.from operatörünü kullanarak :

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

Tekrarlanan bir dizeye ihtiyacınız varsa String.prototype.repeat komutunu kullanabilirsiniz .

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

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

6
Bu en iyi cevap olmalı. Yani ES6! Çok harika!
Gergely Fehérvári

3
Yineleyiciye (i) ihtiyacınız yoksa, bunu yapmak için hem anahtarı hem de değeri hariç tutabilirsiniz:[...Array(10)].forEach(() => console.log('looping 10 times');
Sterling Bourne

9
Yani N öğesinin tamamını sadece atmak için mi ayırıyorsunuz?
Kugel

2
Kugel tarafından yapılan önceki yoruma hitap eden var mı? Aynı şeyi merak ediyordum
Arman

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

Bu işe yarıyor, bu yüzden harika! Ancak, fazladan çalışmaya ihtiyaç duyulması anlamında biraz çirkin ve bu Arrayanahtarların kullanıldığı şey değil .
at.

@at. aslında. Ama [0..x]JS'de cevabımdan daha özlü bir haskell'in eşanlamlı olduğundan emin değilim .
zerkms

bundan daha kısa hiçbir şeyin olmadığı konusunda doğru olabilirsiniz.
at.

Tamam, bunun neden Array.prototype.keysve arasındaki farklar verildiğini anlıyorum Object.prototype.keys, ama ilk bakışta kafa karıştırıcı.
Mark Reed

1
@ cchamberlain ES2015'de TCO ile (olsa hiçbir yerde uygulanmadı mı?) endişe daha az olabilir, ama gerçekten :-)
zerkms

29

Bence en iyi çözüm kullanmaktır let:

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

Bu, iher vücut değerlendirmesi için yeni (değişken) bir değişken yaratacaktır ve bunun, ibaşka bir yerden değil, yalnızca o döngü sözdizimindeki artış ifadesinde değiştirilmesini sağlar .

Bir tür hile yapabilir ve kendi jeneratörümü yapabilirim. En azından i++gözden uzak :)

Bu yeterli imo olmalı. Saf dillerde bile tüm işlemler (veya en azından tercümanları) mutasyon kullanan ilkellerden yapılır. Düzgün kapsamlandırıldığı sürece, bunun neyin yanlış olduğunu göremiyorum.

İyi olmalısın

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

Ama ben ++operatörü kullanmak istemiyorum ya da hiç değişken değişkenim yok.

O zaman tek seçeneğiniz özyineleme kullanmaktır. Bu jeneratör işlevini değiştirilebilir olmadan da tanımlayabilirsiniz i:

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

Ama bu benim için aşırı dolu gibi görünüyor ve performans sorunları olabilir (kuyruk çağrısının ortadan kaldırılması mümkün olmadığından return yield*).


1
Bu seçeneği seviyorum - güzel ve basit!
DanV

2
Bu basit ve noktaya ve yukarıda birçok cevap gibi bir dizi ayırmaz
Kugel

@Kugel İkincisi yığın üzerinde tahsis edebilir
Bergi

Kuyruk çağrısı optimizasyonunun burada çalışıp çalışmayacağından emin değilim @Bergi
Kugel



11

Yanıt: 09 Aralık 2015

Şahsen, kabul edilen cevabı hem kısa (iyi) hem de kısa (kötü) buldum. Bu ifadenin öznel olabileceğini takdir edin, bu yüzden lütfen bu yanıtı okuyun ve kabul edip etmediğinizi görün

Soruda verilen örnek, Ruby'nin benzeri bir şeydi:

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

Bunu JS'de aşağıdakileri kullanarak ifade etmek aşağıdakilere izin verir:

times(x)(doStuff(i));

İşte kod:

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

Bu kadar!

Basit örnek kullanım:

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

times(3)(cheer);

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

Alternatif olarak, kabul edilen cevap örneklerini takip ederek:

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'

Yan not - Bir aralık işlevi tanımlama

Temelde çok benzer kod yapılarını kullanan benzer / ilgili bir soru, alt çekirdeğin aralık işlevine benzer bir şey olan (çekirdek) JavaScript'te uygun bir Aralık işlevi olabilir.

X'den başlayarak n numaralı bir dizi oluşturun

Vurgulamak

_.range(x, x + n)

ES2015

Birkaç alternatif:

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

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

N = 10, x = 1 kullanarak demo:

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

Hızlı bir testte, yukarıdaki her bir çözüm ve doStuff fonksiyonumuzu kullanarak her biri bir milyon kez çalıştığında, eski yaklaşımın (Array (n) .fill ()) biraz daha hızlı olduğu kanıtlandı.


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

Bu sürüm OP'nin değişmezlik gereksinimini karşılar. Kullanım durumunuza bağlı olarak kullanmak reduceyerine kullanmayı da düşünün map.

Bu, prototipinizde küçük bir mutasyonun sakıncası yoksa da bir seçenektir.

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

Şimdi bunu yapabiliriz

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

Öneri için arcseldon'a +1 .fill.


Olarak, aşağı Oy dolgu yöntemi IE veya Opera veya PhantomJS desteklenmez
morhook

8

İşte başka bir iyi alternatif:

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

Tercihen, @Dave Morse yorumlarda belirtildiği gibi map, Array.fromfonksiyonun ikinci parametresini şu şekilde kullanarak da çağrıdan kurtulabilirsiniz :

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


2
Bu kabul edilen cevap olmalı! Küçük bir öneri - Array.from ile ücretsiz olarak ihtiyacınız olan harita benzeri işlevselliği zaten elde edersiniz: Array.from({ length: label.length }, (_, i) => (...)) Bu, sadece haritaya bir çağrıyı başlatmak için boş bir geçici dizi oluşturmanızı sağlar.
Dave Morse

7

Öğretebileceğim bir şey değil (ya da kodumda kullanacağım), ama burada bir değişkeni mutasyona uğratmadan codegolf'a layık bir çözüm, ES6'ya gerek yok:

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

Gerçekten faydalı bir cevaptan ziyade ilginç bir kavram kanıtı.


Coudn't Array.apply(null, {length: 10})sadece olmak Array(10)?
Pavlo

1
@ Pavlo, aslında hayır. Dizi (10), 10 uzunluğunda bir dizi oluşturur, ancak içinde tanımlanmış herhangi bir anahtar olmadan, bu durumda forEach yapısını kullanılamaz hale getirir. Ama forEach kullanmazsanız basitleştirilebilir, zerkms'in cevabına bakın (bu ES6'yı kullanır!).
doldt

creative @doldt, ama öğretilebilir ve basit bir şey arıyorum.
at.

5

Partiye geç kaldım, ancak bu soru arama sonuçlarında sık sık ortaya çıktığı için, uzun süre değil (herhangi bir kod tabanı IMO için ideal) okunabilirlik açısından en iyi olduğunu düşündüğüm bir çözüm eklemek istiyorum. . Değişir, ama ben KISS ilkeleri için bu ödünleşmeyi yapardım.

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

3
Sadece daha üst düzey bir lambda fetiş partisi olarak tanımlayabileceğim şeyde aklın sesi olduğun için teşekkür ederim. Ben de Google yolunda zararsız bir ilk hit sonra bu soru-cevap sona erdi ve aklıma burada cevapların çoğu tarafından hızla kutsallık vardı. Sizinkiler, listede basit bir soruna doğrudan bir çözüm olduğunu düşüneceğim ilk çözümdür.
Martin Devillers

Bu konuyla ilgili tek sorun times, döngü içindeki değişkeni kullanmak istiyorsanız biraz mantıksız olmasıdır . Belki countdowndaha iyi bir adlandırma olurdu. Aksi takdirde, sayfadaki en temiz ve en açık cevap.
Tony Brasunas

3

Afaik, ES6'da Ruby'nin timesyöntemine benzer bir mekanizma yok . Ancak özyineleme kullanarak mutasyondan kaçınabilirsiniz:

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


Bu yaklaşımı seviyorum, özyinelemeyi seviyorum. Ancak, yeni JavaScript kullanıcılarının döngülerini göstermek için daha basit bir şey isterim.
at.

3

Bir kütüphane kullanmaya istekli iseniz, aynı zamanda sade_.times veya alt çizgi de vardır_.times :

_.times(x, i => {
   return doStuff(i)
})

Bunun sonuçların bir dizisini döndürdüğünü unutmayın, bu yüzden daha çok bu yakut gibi:

x.times.map { |i|
  doStuff(i)
}

2

İşlevsel paradigmada repeatgenellikle sonsuz bir özyinelemeli işlev vardır. Bunu kullanmak için ya tembel değerlendirmeye ya da devam etme tarzına ihtiyacımız var.

Tembel değerlendirilen fonksiyon tekrarı

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

Javascript tembel değerlendirme elde etmek için bir thunk (bağımsız değişkenler olmadan bir işlev) kullanın.

Devam geçiş tarzı ile fonksiyon tekrarı

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 başlangıçta biraz korkutucu. Bununla birlikte, her zaman aynı modeli takip eder: Son argüman, kendi bedenini çağıran devam (fonksiyon) dur k => k(...). CPS tersyüz uygulamayı döndüğünü Lütfen not, yani take(8) (repeat...)olur k(take(8)) (...)nerede kkısmen uygulanmaktadır repeat.

Sonuç

Tekrarı ( repeat) sonlandırma koşulundan ( take) ayırarak esneklik kazanırız - endişelerin acı sonuna kadar ayrılması: D


1

Bu çözümün avantajları

  • En kolay okunan / kullanılan (imo)
  • Dönüş değeri bir toplam olarak kullanılabilir veya yok sayılabilir
  • Düz es6 sürümü, ayrıca kodun TypeScript sürümüne bağlantı

Dezavantajları - Mutasyon. İçsel olmak sadece umrumda değil, belki bazıları da istemez.

Örnekler ve 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
}

TypeScipt sürümü
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011


0

fonksiyonel yönü ele almak:

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
"işlevsel yönü ele almak" ve daha sonra değişebilen bir zorunlu döngü kullanmak i. O zaman timesdüz eski üzerinde bile kullanmanın nedeni nedir for?
zerkms

gibi yeniden var twice = times(2);.
Nina Scholz

Öyleyse neden sadece foriki kez kullanmıyorsunuz ?
zerkms

kullanmak için korkmuyorum. soru, değişken değişken kullanmamaktı. ancak sonuç her zaman değişken olarak önbellekleme türüdür.
Nina Scholz

1
"bir değişken kullanmamalıydı" --- ve hala kullanıyorsun - i++. Bir işlevde kabul edilemez bir şeyin sarılmasının onu daha iyi hale getirdiği açık değildir.
zerkms

0

Jeneratörler? Özyineleme? Neden mutatine bu kadar çok nefret ediyorsun? ;-)

"Gizlediğimiz" sürece kabul edilebilirse, tekli bir operatör kullanımını kabul edin ve işleri basit tutabiliriz :

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

Tıpkı yakutta olduğu gibi:

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

2
Thumbs up: "Neden mutatin üzerinde bu kadar çok nefret ediyorsun?"
Sarreph

1
Basitlik için başparmak yukarıya, maymunsu ile yakut tarzı biraz fazla gitmek için başparmak aşağı. Sadece o kötü kötü maymunlara hayır de.
mrm

1
@mrm bu "maymun yama", bu sadece bir uzantı değil mi? Kucaklama ve genişletme :)
conny

Hayır. Numaraya (veya Dize veya Dizi veya yazmadığınız başka bir sınıf) işlev eklemek, tanım olarak, çoklu dolgular veya maymun yamalarıdır ve hatta çoklu dolgular önerilmez. "Maymun yaması", "çoklu dolgu" ve önerilen alternatif "ponyfill" tanımlarını okuyun. İstediğin bu.
mrm

Number'ı genişletmek için şunları yaparsınız: class SuperNumber, Number {times (fn) {için (let i = 0; i <this; i ++) {fn (i); }}}
Alexander

0

@Tieme'in cevabını bir yardımcı işlevle tamamladım.

TypeScript'te:

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

Şimdi çalıştırabilirsiniz:

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

0

Bunu ben yaptım:

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

Kullanımı:

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

iKullanışlı Eğer görüntülerin bir x miktarını önceden yüklemek gerekiyorsa - değişken o ilmek kaç kez miktarını döndürür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.