Belum pernah terlihat sebelumnya C ++ for loop


164

Saya mengonversi algoritma C ++ ke C #. Saya menemukan ini untuk loop:

for (u = b.size(), v = b.back(); u--; v = p[v]) 
b[u] = v;

Ini tidak memberikan kesalahan dalam C ++, tetapi tidak di C # (tidak dapat mengkonversi int ke bool). Saya benar-benar tidak tahu ini untuk loop, di mana kondisinya?

Bisakah seseorang tolong jelaskan?

PS. Hanya untuk memeriksa, untuk mengadaptasi VECTOR ke LIST, apakah b.back () sesuai dengan b [b.Count-1]?


35
Dimana kondisinya? Itu pasti u--. Semi-titik dua digunakan untuk membatasi berbagai bagian forpernyataan.
David Heffernan

77
Ini adalah loop yang cukup normal. C # tidak mengonversi angka menjadi bool secara implisit sehingga Anda perlu mengubah kondisi menjadi; u-- != 0;
R. Martinho Fernandes

28
@Jessie Good - Kode yang baik tidak ada hubungannya dengan apa yang diperbolehkan dalam bahasa, itu ada hubungannya dengan berapa lama seorang rekan kerja yang tidak berkepentingan untuk membaca kode tersebut. Jika itu menyebabkan segala jenis kebingungan, itu bukan solusi terbaik, bahkan jika itu legal. Seringkali solusi yang lebih bertele-tele jauh lebih baik daripada solusi singkat, dan sebagian besar penyusun kompilasi akan melakukan hal yang sama.
Bill K

18
Saya berharap bahwa, setelah mengkonversi kode, Anda memberikan variabel nama yang lebih baik dari b, u, v, dll Satu-satunya alasan mereka diberi nama dengan cara ini adalah karena seseorang ingin terlihat pintar dengan membuat kode mereka terbaca.
Dan

33
@ houbysoft: ini adalah masalah stackoverflow umum. Jika Anda mengajukan pertanyaan yang sangat terperinci, diteliti dengan baik, dan menarik dalam domain penelitian tertentu, yang mengarah ke solusi untuk masalah yang sulit dan menarik, dan Anda menjawab pertanyaan seperti itu setelah melewati hari-hari penelitian yang sulit, Anda hanya akan mendapatkan beberapa selusin pengunjung dan satu atau dua suara positif dari beberapa ahli di bidang ini. Jika Anda ingin mendapatkan banyak rep dengan cepat, Anda harus bertanya dan menjawab pertanyaan seperti ini. "Bagaimana cara menambahkan dua angka di php", "apa doartinya di C ++" - akan mendapatkan ribuan klik dari pemula yang mencari tutorial.
vsz

Jawaban:


320

Kondisi forloop berada di tengah - di antara dua titik koma ;.

Dalam C ++ tidak masalah untuk menempatkan hampir semua ekspresi sebagai suatu kondisi: apa pun yang dievaluasi menjadi nol berarti false; berarti bukan nol true.

Dalam kasus Anda, syaratnya adalah u--: ketika Anda mengonversi ke C #, cukup tambahkan != 0:

for (u = b.size(), v = b.back(); u-- != 0; v = p[v]) 
    b[u] = v; //                     ^^^^ HERE

55
Ngomong-ngomong, Thomas mungkin bingung dengan penggunaan koma juga, itu sangat berbeda dari semi-colon, itu memungkinkan Anda melakukan banyak hal dalam satu bagian dari for loop (dalam hal ini, ia menginisialisasi dua variabel). Terakhir kali saya memeriksa konstruksi yang tidak biasa ini tidak dianggap sebagai solusi yang paling mudah dibaca dan oleh karena itu mungkin disukai oleh beberapa orang.
Bill K

2
Jika saya ingat dengan benar, dan ekspresi yang mengandung koma memiliki nilai subekspresi terakhir di sebelah kanan. Ini berasal dari C dan dapat digunakan dalam ekspresi apa pun, tidak hanya dalam for for loop.
Giorgio

7
@Roger Ini bukan loop yang sama karena Anda menurunkan u di akhir loop, alih-alih dari awal (yaitu setelah b [u] = v alih-alih sebelumnya). Bahkan Anda harus menginisialisasi dengan u = b.size() - 1gantinya.
Didier L

Fyi: Saya baru saja mendorong Anda melebihi batas 500 ribu. Apakah itu seperti menjadi pelanggan sepersejuta di suatu tempat dan memenangkan sesuatu? Dalam banyak kasus: banyak ucapan selamat; selalu menengadah ke jawaban yang tepat dan tajam! Teruskan; bertemu Anda membuat 1 juta hal ... akhir abad ini.
GhostCat

165

Banyak jawaban yang akurat, tapi saya pikir ada baiknya menuliskan loop sementara yang setara.

for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

Setara dengan:

u = b.size();
v = b.back();
while(u--) {
   b[u] = v;
   v = p[v];
}

Anda dapat mempertimbangkan refactoring ke format while () saat Anda menerjemahkan ke C #. Menurut saya itu lebih jelas, lebih sedikit jebakan untuk programmer baru, dan sama-sama efisien.

Seperti orang lain telah menunjukkan - tetapi untuk membuat jawaban saya lengkap - untuk membuatnya bekerja di C # Anda akan perlu untuk perubahan while(u--)ke while(u-- != 0).

... atau while(u-- >0)kalau-kalau Anda mulai negatif. (Oke, b.size()tidak akan pernah menjadi negatif - tetapi pertimbangkan kasus umum di mana mungkin sesuatu yang lain diinisialisasi).

Atau, untuk membuatnya lebih jelas:

u = b.size();
v = b.back();
while(u>0) {
   u--;
   b[u] = v;
   v = p[v];
}

Lebih baik jelas daripada singkat.


29
Jawaban ini tidak hanya mengklarifikasi kode buruk tetapi juga memberikan alternatif yang baik. 1 naik !!
polvoazul

2
Sebagai tambahan, saya akan berhati-hati dengan while (u-- >0)formulir. Jika spasi menjadi kacau, Anda mungkin berakhir dengan loop "turun ke nol":, while (u --> 0)yang cenderung membingungkan semua orang pada pandangan pertama. ( Saya tidak yakin apakah itu valid C #, tetapi dalam C, dan saya pikir mungkin juga dalam C ++? )
Izkata

9
Saya tidak berpikir kode Anda lebih jelas. Maksud dari forbukannya whilejustru bahwa Anda meletakkan inisialisasi dan kenaikan / penurunan dalam satu pernyataan, dan itu tidak selalu membuat kode lebih sulit untuk dipahami. Kalau tidak, kita tidak boleh menggunakan forsama sekali.
musiphil

1
Penting juga untuk menulis ulang kode sesedikit mungkin. (Lagipula untuk loop bisa berubah.) Anda telah meletakkan "u--" di dua tempat yang terpisah, dan loop tidak benar-benar lebih jelas (saya bisa melihat apa yang dilakukan loop dalam satu pandangan; saya harus memindai dengan beberapa baris). Menjadi singkat juga memiliki manfaat. Jangan mengecilkan mereka. Meski begitu, contoh yang baik tentang bagaimana lagi itu bisa ditulis. Itu membuatnya lebih mudah untuk memahami siapa saja yang tidak terbiasa dengan pernyataan dalam C ++ (atau bahkan mungkin sama sekali).
NotKyon

2
@Izkata: Ini bukan PERNAH operator, itu diurai sebagai --token diikuti oleh >token. Dua operator terpisah. Lingkaran "turun ke nol" hanyalah kombinasi langsung dari pengurangan setelah dan lebih besar dari. Kelebihan operator C ++ tidak membuat operator baru, melainkan hanya menggunakan yang sudah ada.
Ben Voigt

66

Syaratnya u--;, karena berada di posisi kedua untuk instruksi.

Jika nilai u--;berbeda dari 0, itu akan ditafsirkan sebagai true(yaitu, secara implisit dicor ke nilai boolean true). Jika, sebagai gantinya, nilainya 0, itu akan dilemparkan ke false.

Ini kode yang sangat buruk .

Pembaruan: Saya membahas penulisan loop "untuk" di posting blog ini . Rekomendasinya dapat diringkas dalam paragraf berikut:

A for loop adalah yang praktis, dapat dibaca (setelah Anda terbiasa) dan konstruksinya singkat, tetapi Anda harus menggunakannya dengan baik. Karena sintaksisnya yang tidak umum, menggunakannya dengan cara yang terlalu imajinatif bukanlah ide yang baik.

Semua bagian dari for loop harus pendek dan mudah dibaca. Nama variabel harus dipilih untuk membuatnya mudah dimengerti.

Contoh ini jelas melanggar rekomendasi ini.


3
Atau itu akan berhenti pada Anda == 0 mungkin ...?
Thomas

2
Tidak; di C ++ ada konversi implisit dari int ke bool, dengan 0 mengkonversi ke false. Loop akan berakhir ketika u-- == 0. Dalam C #, tidak ada konversi tersirat sehingga Anda harus secara eksplisit mengatakan u-- == 0. EDIT: Ini sebagai respons terhadap komentar pertama Anda.
Chris

48
Ini adalah kode yang mengerikan , karena satu alasan yang sangat sederhana; Anda tidak dapat dengan mudah memahaminya ketika Anda membacanya. Ini "pintar", "retas"; itu menggunakan kombinasi struktur pengkodean, dan pengetahuan tentang bagaimana mereka bekerja di belakang layar, untuk membuat struktur yang melakukan pekerjaan tetapi yang menentang pemahaman, karena itu tidak dalam bentuk yang dibayangkan oleh penulis bahasa dan yang dikomunikasikan kepada sebagian besar pengguna bahasa.
KeithS

4
Jawaban yang sangat bagus. Saya akan memberi +1 ... jika bukan "Ini kode yang sangat buruk." pernyataan;)
Sandman4

14
Saya tidak mengerti mengapa begitu banyak orang berpikir bahwa u-- adalah kode yang benar-benar buruk hanya karena hilang (tersirat dalam C ++) ! = 0 . Tentunya siapa pun yang bekerja dengan kode akan sangat menyadari bahwa 0 = salah, setiap nilai lainnya = benar . Ada jauh lebih banyak ruang untuk kebingungan mengenai kenaikan sebelum atau sesudah Anda , atau orang mungkin berasumsi bahwa u = b.ukuran () akan selalu dieksekusi sebelum v = b.kembali () (pemahaman saya adalah urutan eksekusi yang tidak ditentukan, tapi saya berdiri untuk dikoreksi).
FumbleFingers

23

Ini akan menjadi bentuk C # dari loop Anda.

// back fetches the last element of vector in c++.
for (u = b.size(), v = b.back(); (u--) != 0; v = p[v]) 
{      
  b[u] = v;      
}

Cukup ganti yang setara dengan ukuran () dan kembali ().

Apa yang dilakukannya adalah membalik daftar dan menyimpannya dalam sebuah array. Tetapi dalam C # kita langsung memiliki fungsi yang ditentukan sistem untuk ini. Jadi Anda tidak perlu menulis loop ini juga.

b = b.Reverse().ToArray();

1
dengan v = b.back();mengeluarkan intializer bukankah Anda mengubah cara kerjanya, karena v = p[v]ditimpa oleh
João Portela

v = p [v] tidak akan berpengaruh pada hasil akhir karena baris ini akan dieksekusi pada akhirnya. Dan setelah itu v tidak disebut dalam loop. Baris ini ada hanya untuk menunjukkan bagaimana loop dikonversi dari c ++ ke C #.
Narendra

1
dalam kode c ++ v = b.back();dieksekusi sekali sebelum iterasi dimulai dan v = p[v]dieksekusi pada awal setiap iterasi. Dalam versi C # v = p[v]masih dieksekusi pada awal setiap iterasi tetapi v = b.back();dieksekusi tepat setelah itu, mengubah nilai vuntuk instruksi berikutnya b[u] = v;. (mungkin pertanyaannya diedit setelah Anda membacanya)
João Portela

5
@ Rain Masalahnya adalah v = b.back(). Anda memilikinya mengeksekusi di setiap loop iterasi bukan hanya yang pertama - kita tidak tahu apa yang back()terjadi (apakah ada efek samping? Apakah itu mengubah representasi internal b?), Jadi loop ini tidak setara dengan yang ada di pertanyaan.
Izkata

1
@Rain Persis, lihat lebih dekat sendiri. Langkah inisialisasi terjadi hanya sekali sebelum loop dimulai, bukan pada awal setiap iterasi . Kode Anda akan benar jika v = b.back()dipindahkan di luar loop, di atasnya. (Juga, jika Anda mencoba menanggapi seseorang, gunakan @di depan nama mereka, jadi kami mendapat pemberitahuan)
Izkata


14

Kondisi adalah hasil dari u--, yang merupakan nilai usebelum dikurangi.

Dalam C dan C ++, sebuah int dapat dikonversi menjadi bool dengan secara implisit melakukan != 0perbandingan (0 adalah false, yang lainnya true).

b.back()adalah elemen terakhir dalam sebuah wadah, yaitu b[b.size() - 1], kapan size() != 0.


11

Dalam C semuanya non-nol truedalam konteks "boolean", seperti kondisi akhir loop atau pernyataan kondisional. Dalam C # Anda harus membuat cek eksplisit: u-- != 0.


Ini bukan pertanyaan OP. Dia bertanya tentang evaluasi kondisi terminal (yaitu 'u--' dalam kasus ini).
ApplePie

6

Seperti yang dinyatakan oleh orang lain, fakta bahwa C ++ memiliki casting implisit ke boolean berarti kondisional u--, yang akan benar jika nilainya tidak nol.

Perlu ditambahkan, bahwa Anda memiliki asumsi yang salah dalam bertanya "di mana kondisional". Dalam C ++ dan C # (dan bahasa sintaksis lainnya yang serupa) Anda dapat memiliki kondisi kosong. Dalam hal ini selalu bernilai true, sehingga loop terus selamanya, atau sampai beberapa keluar kondisi lain (melalui return, breakatau throw).

for(int i = 0; ; ++i)
  doThisForever(i);

Memang, bagian mana pun dari pernyataan for dapat diabaikan, dalam hal ini tidak dilakukan.

Secara umum, for(A; B; C){D}atau for(A; B; C)D;menjadi:

{A}
loopBack:
if(!(B))
  goto escapeLoop;
{D}
{C}
goto loopBack;
escapeLoop:

Satu atau lebih dari A, B, C atau D dapat diabaikan.

Sebagai akibatnya, beberapa bantuan for(;;) untuk loop tak terbatas. Saya melakukannya karena walaupun while(true)lebih populer, saya membacanya sebagai "sampai kebenaran berakhir menjadi kenyataan", yang terdengar agak apokaliptik dibandingkan dengan pembacaan saya for(;;) sebagai "selamanya".

Ini masalah selera, tetapi karena saya bukan satu-satunya orang di dunia yang menyukainya for(;;), perlu mengetahui artinya.


4

semua jawaban sudah benar: -

untuk loop dapat digunakan dalam berbagai cara sebagai berikut:

Single Statement inside For Loop
Multiple Statements inside For Loop
No Statement inside For Loop
Semicolon at the end of For Loop
Multiple Initialization Statement inside For
Missing Initialization in For Loop
Missing Increment/Decrement Statement
Infinite For Loop
Condition with no Conditional Operator.

4
for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

Dalam kode di atas, udan vdiinisialisasi dengan b.size()dan b.back().

Setiap kali kondisi diperiksa, ia mengeksekusi pernyataan penurunan juga yaitu u--.

The forLoop akan keluar ketika uakan menjadi 0.


3

Kesalahan yang ditemui dalam C # itu sendiri menghapus keraguan. Pencarian for-loop untuk a

SALAH

kondisi untuk mengakhiri. Dan seperti yang kita ketahui,

(BOOL) FALSE = (int) 0

tetapi C # tidak dapat memproses ini sendiri tidak seperti C ++. Jadi kondisi yang Anda cari adalah

kamu--

tetapi Anda harus secara eksplisit memberikan kondisi dalam C # as

kamu--! = 0

atau

kamu--> 0

Tetapi tetap mencoba untuk menghindari praktik pengkodean semacam ini. Itu

sementara lingkaran

dinyatakan di atas dalam jawaban adalah salah satu versi yang paling disederhanakan dari Anda

untuk-loop.


@Downvoter: Downvoting baik-baik saja sejauh Anda tidak puas dengan solusi tetapi pada saat yang sama, mohon luangkan waktu untuk menyatakan alasannya, sehingga jawaban dapat ditingkatkan.
Abhineet

3

Jika Anda terbiasa dengan C / C ++ kode ini tidak terlalu sulit dibaca, meskipun cukup singkat dan tidak terlalu bagus. Jadi izinkan saya menjelaskan bagian-bagian yang lebih Cism daripada yang lain. Pertama, sintaks umum dari C untuk loop terlihat seperti ini:

for (<initialization> ; <condition>; <increment>)
{
    <code...>
}

Kode inisialisasi dijalankan sekali. Kemudian kondisi diuji sebelum setiap loop dan terakhir kenaikan dipanggil setelah setiap loop. Jadi, dalam contoh Anda, Anda akan menemukan kondisinyau--

Mengapa u--berfungsi sebagai kondisi dalam C dan bukan C #? Karena C secara implisit mengubah banyak hal terlalu bodoh dan dapat menyebabkan masalah. Untuk bilangan apa pun yang bukan nol adalah benar dan nol adalah salah. Jadi itu akan menghitung mundur dari b.ukuran () - 1 ke 0. Memiliki efek samping dalam kondisi agak mengganggu dan akan lebih baik untuk meletakkannya di bagian kenaikan untuk loop, meskipun banyak C kode melakukan ini. Jika saya menulisnya, saya akan melakukannya lebih seperti ini:

for (u = b.size() - 1, v = b.back(); u>=0; --u) 
{
    b[u] = v;
    v = p[v]
}

Alasannya adalah, setidaknya bagi saya, lebih jelas. Setiap bagian dari for loop melakukan tugasnya dan tidak ada yang lain. Dalam kode asli syaratnya adalah memodifikasi variabel. Bagian kenaikan sedang melakukan sesuatu yang harus di blok kode dll.

Operator koma mungkin juga melempar Anda untuk loop. Dalam C sesuatu seperti x=1,y=2tampak seperti satu pernyataan sejauh menyangkut kompiler dan cocok dengan kode inisialisasi. Itu hanya mengevaluasi setiap bagian dan mengembalikan nilai yang terakhir. Jadi misalnya:

std::cout << "(1,2)=" << (1,2) << std::endl;

akan mencetak 2.


Masalah dengan penulisan ulang Anda adalah bahwa jika b.size () tidak ditandatangani, itu akan beralih untuk waktu yang sangat lama. (Juga, Anda kehilangan titik koma.) Tetapi setidaknya Anda tidak mengambil pendekatan "'Dick dan Jane' adalah bahasa Inggris yang baik" yang dilakukan oleh begitu banyak jawaban dan komentar lainnya, sambil memuji penulisan ulang yang tidak masuk akal yang hanya lebih mudah untuk dibaca oleh orang baru dan programmer tidak terampil lainnya.
Jim Balter
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.