Cara khas untuk mengulang x
kali dalam JavaScript adalah:
for (var i = 0; i < x; i++)
doStuff(i);
Tetapi saya tidak ingin menggunakan ++
operator atau memiliki variabel yang bisa berubah-ubah sama sekali. Jadi apakah ada cara, di ES6, untuk mengulang x
kali dengan cara lain? Saya suka mekanisme Ruby:
x.times do |i|
do_stuff(i)
end
Adakah yang serupa dengan JavaScript / ES6? Saya bisa menipu dan membuat generator sendiri:
function* times(x) {
for (var i = 0; i < x; i++)
yield i;
}
for (var i of times(5)) {
console.log(i);
}
Tentu saja saya masih menggunakan i++
. Setidaknya tidak terlihat :), tapi saya berharap ada mekanisme yang lebih baik di ES6.
Jawaban:
BAIK!
Kode di bawah ini ditulis menggunakan sintaks ES6 tetapi bisa dengan mudah ditulis dalam ES5 atau bahkan kurang. ES6 bukan persyaratan untuk membuat "mekanisme untuk mengulang x kali"
Jika Anda tidak memerlukan iterator di callback , ini adalah implementasi paling sederhana
Jika Anda benar-benar membutuhkan iterator , Anda dapat menggunakan fungsi dalam bernama dengan parameter penghitung untuk beralih untuk Anda
Tetapi sesuatu harus merasa tidak enak tentang itu ...
if
pernyataan cabang tunggal jelek - apa yang terjadi di cabang lain?undefined
- indikasi fungsi tidak murni dan efek samping"Apakah tidak ada cara yang lebih baik?"
Ada. Pertama mari kita meninjau kembali implementasi awal kita
Tentu, ini sederhana, tetapi perhatikan bagaimana kita menelepon
f()
dan tidak melakukan apa-apa dengannya. Ini benar-benar membatasi jenis fungsi yang dapat kami ulangi berkali-kali. Bahkan jika kita memiliki iterator yang tersedia,f(i)
tidak jauh lebih fleksibel.Bagaimana jika kita mulai dengan prosedur pengulangan fungsi yang lebih baik? Mungkin sesuatu yang lebih baik menggunakan input dan output.
Pengulangan fungsi generik
Di atas, kami mendefinisikan
repeat
fungsi generik yang mengambil input tambahan yang digunakan untuk memulai aplikasi berulang fungsi tunggal.Melaksanakan
times
denganrepeat
Nah ini mudah sekarang; hampir semua pekerjaan sudah dilakukan.
Karena fungsi kita mengambil
i
sebagai input dan kembalii + 1
, ini secara efektif berfungsi sebagai iterator kita yang kita lewatif
setiap waktu.Kami telah memperbaiki daftar masalah kami juga
if
pernyataan cabang tunggal yang jelekundefined
Operator koma JavaScript,
Jika Anda kesulitan melihat bagaimana contoh terakhir bekerja, itu tergantung pada kesadaran Anda tentang salah satu sumbu pertempuran JavaScript tertua; yang operator koma - singkatnya, itu mengevaluasi ekspresi dari kiri ke kanan dan mengembalikan nilai dari ekspresi dievaluasi terakhir
Dalam contoh di atas, saya menggunakan
yang hanya cara penulisan yang ringkas
Optimasi Panggilan Ekor
Seberapa seksi implementasi rekursif ini, pada titik ini tidak bertanggung jawab bagi saya untuk merekomendasikan mereka mengingat bahwa tidak ada JavaScript VM yang dapat saya pikirkan mendukung eliminasi panggilan ekor yang tepat - babel digunakan untuk mentransmisikannya, tetapi sudah dalam "rusak; akan reimplement "status lebih dari setahun.
Karena itu, kita harus meninjau kembali implementasi kita
repeat
untuk membuatnya menjadi stack-safe.Kode di bawah ini memang menggunakan variabel yang bisa berubah
n
danx
tetapi perhatikan bahwa semua mutasi dilokalkan kerepeat
fungsi - tidak ada perubahan status (mutasi) yang terlihat dari luar fungsiIni akan membuat banyak dari Anda mengatakan "tapi itu tidak berfungsi!" - Aku tahu, santai saja. Kita dapat mengimplementasikan gaya-Clojure
loop
/recur
antarmuka untuk perulangan ruang konstan menggunakan ekspresi murni ; tidak ada yang sepertiwhile
itu.Di sini kita mengabstraksi
while
denganloop
fungsi kita - mencarirecur
tipe khusus untuk menjaga loop tetap berjalan. Ketika non-recur
type ditemukan, loop selesai dan hasil perhitungan dikembalikansumber
g => g(g)(x)
). Apakah ada manfaat dari fungsi tingkat tinggi daripada fungsi tingkat pertama, seperti dalam solusi saya?Menggunakan operator Penyebaran ES2015 :
[...Array(n)].map()
Atau jika Anda tidak membutuhkan hasilnya:
Atau menggunakan operator ES2015 Array.from :
Array.from(...)
Perhatikan bahwa jika Anda hanya perlu sebuah string diulang Anda dapat menggunakan String.prototype.repeat .
sumber
Array.from(Array(10), (_, i) => i*10)
[...Array(10)].forEach(() => console.log('looping 10 times');
sumber
Array
kunci digunakan.[0..x]
JS lebih ringkas daripada jawaban saya.Array.prototype.keys
danObject.prototype.keys
, tetapi pada awalnya membingungkan.Saya pikir solusi terbaik adalah menggunakan
let
:Itu akan membuat
i
variabel (bisa berubah) baru untuk setiap evaluasi tubuh dan memastikan bahwai
hanya diubah dalam ekspresi kenaikan dalam sintaks loop itu, bukan dari tempat lain.Itu seharusnya cukup imo. Bahkan dalam bahasa murni semua operasi (atau setidaknya, juru bahasa mereka) dibangun dari primitif yang menggunakan mutasi. Selama ini memiliki cakupan yang tepat, saya tidak bisa melihat apa yang salah dengan itu.
Anda harus baik-baik saja dengan
Maka satu-satunya pilihan Anda adalah menggunakan rekursi. Anda dapat mendefinisikan fungsi generator tanpa bisa berubah
i
juga:Tapi itu tampaknya terlalu berat bagi saya dan mungkin memiliki masalah kinerja (karena eliminasi panggilan ekor tidak tersedia untuk
return yield*
).sumber
Cuplikan ini akan
console.log
test
4 kali.sumber
Saya pikir ini sangat sederhana:
atau
sumber
Jawaban: 09 Desember 2015
Secara pribadi, saya menemukan jawaban yang diterima baik singkat (baik) dan singkat (buruk). Menghargai pernyataan ini mungkin subjektif, jadi silakan baca jawaban ini dan lihat apakah Anda setuju atau tidak
Contoh yang diberikan dalam pertanyaan adalah sesuatu seperti Ruby:
Mengekspresikan ini dalam JS menggunakan di bawah ini akan memungkinkan:
Ini kodenya:
Itu dia!
Contoh penggunaan sederhana:
Atau, ikuti contoh jawaban yang diterima:
Catatan samping - Menentukan fungsi rentang
Pertanyaan serupa / terkait, yang menggunakan konstruksi kode yang secara fundamental sangat mirip, mungkin adakah fungsi Range yang nyaman dalam JavaScript (inti), sesuatu yang mirip dengan fungsi rentang garis bawah.
Buat array dengan n angka, mulai dari x
Menggarisbawahi
ES2015
Beberapa alternatif:
Demo menggunakan n = 10, x = 1:
Dalam tes cepat saya berlari, dengan masing-masing di atas berjalan satu juta kali masing-masing menggunakan fungsi solusi dan doStuff kami, pendekatan sebelumnya (Array (n) .fill ()) terbukti sedikit lebih cepat.
sumber
Versi ini memenuhi persyaratan OP untuk kekekalan. Juga mempertimbangkan menggunakan
reduce
bukannyamap
tergantung pada kasus penggunaan Anda.Ini juga merupakan pilihan jika Anda tidak keberatan sedikit mutasi dalam prototipe Anda.
Sekarang kita bisa melakukan ini
+1 ke arcseldon untuk
.fill
saran.sumber
Berikut adalah alternatif lain yang baik:
Lebih disukai, seperti yang ditunjukkan @Dave Morse dalam komentar, Anda juga dapat menyingkirkan
map
panggilan, dengan menggunakan parameterArray.from
fungsi kedua seperti:sumber
Array.from
di MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Array.from({ length: label.length }, (_, i) => (...))
Ini menghemat pembuatan array sementara yang kosong hanya untuk memulai panggilan ke peta.Bukan sesuatu yang akan saya ajarkan (atau pernah digunakan dalam kode saya), tapi inilah solusi codegolf-layak tanpa mengubah variabel, tidak perlu untuk ES6:
Lebih dari sekadar bukti konsep yang menarik daripada jawaban yang berguna, sungguh.
sumber
Array.apply(null, {length: 10})
adilArray(10)
?Saya terlambat ke pesta, tetapi karena pertanyaan ini sering muncul dalam hasil pencarian, saya hanya ingin menambahkan solusi yang saya anggap terbaik dalam hal keterbacaan sementara tidak lama (yang ideal untuk IMO basis kode apa pun) . Itu bermutasi, tapi aku akan menukar prinsip KISS.
sumber
times
variabel di dalam loop. Mungkincountdown
akan menjadi penamaan yang lebih baik. Kalau tidak, jawaban paling bersih dan paling jelas di halaman.Afaik, tidak ada mekanisme dalam ES6 mirip dengan
times
metode Ruby . Tetapi Anda dapat menghindari mutasi dengan menggunakan rekursi:Demo: http://jsbin.com/koyecovano/1/edit?js,console
sumber
Jika Anda ingin menggunakan perpustakaan, ada juga tanda bawaan
_.times
atau garis bawah_.times
:Perhatikan ini mengembalikan array hasil, jadi benar-benar seperti ruby ini:
sumber
Dalam paradigma fungsional
repeat
biasanya merupakan fungsi rekursif tak terbatas. Untuk menggunakannya kita perlu evaluasi malas atau gaya kelanjutan lewat.Malas mengevaluasi pengulangan fungsi
Saya menggunakan thunk (fungsi tanpa argumen) untuk mencapai evaluasi malas di Javascript.
Berfungsi pengulangan dengan gaya passing berkelanjutan
CPS sedikit menakutkan pada awalnya. Namun, selalu mengikuti pola yang sama: Argumen terakhir adalah kelanjutan (fungsi), yang memanggil tubuhnya sendiri:
k => k(...)
. Harap dicatat bahwa CPS mengubah aplikasi keluar, yaitutake(8) (repeat...)
menjadik(take(8)) (...)
tempatk
sebagian diterapkanrepeat
.Kesimpulan
Dengan memisahkan repetisi (
repeat
) dari kondisi terminasi (take
) kita mendapatkan fleksibilitas - pemisahan masalah hingga akhir yang pahit: Dsumber
Keuntungan dari solusi ini
Kekurangan - Mutasi. Menjadi internal saja saya tidak peduli, mungkin beberapa yang lain juga tidak.
Contoh dan Kode
Versi TypeScipt
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011
sumber
menangani aspek fungsional:
sumber
i
. Apa alasan untuk menggunakantimes
lebih dari yang biasafor
?var twice = times(2);
.for
dua kali?i++
. Tidak jelas bagaimana membungkus sesuatu yang tidak dapat diterima dalam suatu fungsi membuatnya lebih baik.Generator? Pengulangan? Kenapa begitu banyak yang mau mutatin? ;-)
Jika itu dapat diterima selama kita "menyembunyikan" itu, maka cukup terima penggunaan operator unary dan kita dapat menjaga hal-hal sederhana :
Sama seperti di ruby:
sumber
Saya membungkus jawaban @Tieme dengan fungsi pembantu.
Dalam TypeScript:
Sekarang Anda dapat menjalankan:
sumber
Saya membuat ini:
Pemakaian:
The
i
variabel mengembalikan jumlah kali itu telah dilingkarkan - berguna jika Anda perlu untuk preload x jumlah gambar.sumber