Döngü için C ++ 'dan önce hiç görülmedi


164

Bir C ++ algoritması için C # dönüştürüyordum. Ben döngü için bu rastladım:

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

C ++ 'da hata vermez, ancak C #' da yapar (int boole dönüştürülemez). Bunu gerçekten döngü için anlayamıyorum, durum nerede?

Birisi açıklayabilir mi lütfen?

PS. Sadece kontrol etmek, bir VEKTÖR'ü bir LİSTE uyarlamak için b.back () b [b.Count-1] 'e karşılık gelir mi?


36
Durum nerede? Bu olurdu u--. Yarı-sütunlar forifadenin çeşitli kısımlarını sınırlamak için kullanılır .
David Heffernan

78
Bu oldukça normal bir döngü. C # sayıları dolaylı olarak bools'a dönüştürmez, bu nedenle koşulu dönüştürmeniz gerekir; u-- != 0;
R. Martinho Fernandes

28
@Jessie Good - İyi kodun dilde izin verilebilir olanla hiçbir ilgisi yoktur, kodun okunması için refakatsiz bir iş arkadaşınızın ne kadar sürdüğü ile ilgilidir. Herhangi bir karışıklığa neden olursa, yasal olsa bile mümkün olan en iyi çözüm değildir. Genellikle daha ayrıntılı bir çözüm, ters olandan çok daha iyidir ve çoğu derleyici aynı şekilde her iki şekilde de derlenir.
Bill K

18
Ben kodu değiştirdikten sonra, sen değişkenler daha iyi isim vermek, umut b, u, vvb birisi kendi kod okunamaz hale getirerek akıllı bakmak istedim çünkü bu şekilde adlandırılmıştır tek sebebi.
Dan

33
@houbysoft: Bu genel bir yığın akışı sorunu. Belirli bir araştırma alanında çok ayrıntılı, iyi araştırılmış ve ilginç bir soru sorarsanız, bu zor ve ilginç bir soruna çözüm getirir ve zor bir güne değecek bir araştırmadan sonra böyle bir soruyu cevaplarsanız, sadece birkaç alandaki birkaç uzmandan bir düzine ziyaretçi ve bir veya iki upvotes. Hızlı bir şekilde çok sayıda temsilci kazanmak istiyorsanız, bunun gibi soruları sormak ve cevaplamak zorundasınız. "Nasıl php iki sayı ekleyebilirim", " doC ++ ne anlama gelir" - bir eğitim arayan yeni başlayanlardan binlerce hit alacak.
vsz

Yanıtlar:


320

Durumu foriki noktalı virgül arasındaki - döngü ortasında ;.

C ++ 'da hemen hemen her ifadeyi koşul olarak koymak sorun değildir: sıfıra göre değerlendirilen her şey false; sıfır olmayan araçlar true.

Sizin durumunuzda, koşul u--: C # 'a dönüştürdüğünüzde şunu ekleyin != 0:

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

55
Bu arada, Thomas virgülün kullanımı ile karıştırılmış olabilir, noktalı virgülden çok farklıdır, for döngüsünün bir bölümünde birden fazla şey yapmanıza izin verir (bu durumda, iki değişken başlattı). Bu olağandışı yapıları en son kontrol ettiğimde mümkün olan en okunabilir çözüm olarak görülmedi ve bu nedenle bazıları tarafından kaşlarını çattı.
Bill K

2
Doğru hatırlıyorsam ve virgül içeren ifade sağdaki son alt ifadenin değerine sahiptir. Bu C'den gelir ve yalnızca bir for döngüsünde değil, herhangi bir ifadede kullanılabilir.
Giorgio

7
@Roger Döngünün sonunda u yerine başlangıçta u azalttığınız için bu aynı döngü olmaz (yani, daha önce yerine b [u] = v'den sonra). Aslında bununla başlatmanız gerekir u = b.size() - 1.
Didier L

Fyi: Seni 500K sınırını aştım. Bir yerde bir milyonuncu müşteri olmak ve bir şey kazanmak gibi mi? Her durumda: birçok tebrik; her zaman kesin ve keskin cevaplarınıza bakar! Devam et; Tanıştığımıza tanışalım 1 milyon şey ... bu yüzyılın sonunda.
GhostCat

165

Çok doğru cevaplar, ama sanırım while döngüsü eşdeğerini yazmaya değer.

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

Şuna eşittir:

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

C # diline çevirirken while () biçimine yeniden bakmayı düşünebilirsiniz. Bence bu daha açık, yeni programcılar için daha az tuzak ve aynı derecede etkili.

Diğerleri işaret gibi - ama cevabım eksiksiz yapmak için - bu değişimin gerekir C # çalışması için while(u--)için while(u-- != 0).

... ya da while(u-- >0)negatif çıkmaya başlarsa. (Tamam, b.size()asla olumsuz olmayacaktır - ama belki u başka bir şey başlattı genel bir durum düşünün).

Veya daha da netleştirmek için:

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

Açık olmak, düz olmaktan daha iyidir.


29
Bu cevap sadece kötü kodu açıklamakla kalmıyor, aynı zamanda iyi bir alternatif sunuyor. 1 Yukarı!!
polvoazul

2
Bir yana, while (u-- >0)forma dikkat etmeliyim . Boşluk dağınıksa while (u --> 0), ilk bakışta herkesi karıştırmaya meyilli bir "sıfıra doğru" döngüsüyle sonuçlanabilir. ( Geçerli C # olup olmadığından emin değilim, ancak C'de ve sanırım C ++ 'da da olabilir mi? )
Izkata

9
Kodunuzun mutlaka daha net olduğunu düşünmüyorum. Bunun foryerine nokta while, tam olarak başlatma ve arttırma / azaltmayı tek bir ifadeye koymanızdır ve bu, kodun anlaşılmasını zorlaştırmaz. Aksi takdirde, hiç kullanmamalıyız for.
musiphil

1
Mümkün olduğunca az kod yeniden yazmak da önemlidir. (Sonuçta for döngüsü değişebilir.) İki ayrı yere "u--" koydunuz ve döngü gerçekten daha net değil (for döngüsünün bir bakışta ne yaptığını görebiliyorum; taramak zorundayım) birden çok satır ile). Kısa olmanın da faydaları vardır. Onları küçümsemeyin. Yine de, başka nasıl yazılabileceğine iyi bir örnek. C ++ 'da (ya da belki de) ifadelere alışkın olmayan herkes için anlaşılmasını kolaylaştırır.
NotKyon

2
@Izkata: HİÇ bir operatör değil, bir --jeton ve ardından bir jeton olarak ayrıştırıldı >. İki ayrı operatör. "Sıfıra doğru" bir döngü, azalmanın ardından ve daha büyük olanın basit bir kombinasyonudur. C ++ operatör aşırı yüklemesi yeni operatörler oluşturmaz, sadece mevcut operatörleri yeniden kullanır.
Ben Voigt

66

Durumdur u--;bu ikinci konumda olduğu için, için talimat.

Değeri u--;0'dan farklıysa, true(yani, dolaylı olarak boole değerine çevrilir true) olarak yorumlanacaktır. Bunun yerine, değeri 0 ise, bu değere çevrilir false.

Bu çok kötü bir kod .

Güncelleme: Bu blog yazısında "for" döngülerinin yazılmasını tartıştım . Önerileri aşağıdaki paragraflarda özetlenebilir:

For loop, pratik, okunabilir (alıştıktan sonra) ve kısa bir yapıdır, ancak iyi kullanmanız gerekir. Yaygın olmayan sözdizimi nedeniyle, onu çok yaratıcı bir şekilde kullanmak iyi bir fikir değildir.

For döngüsünün tüm parçaları kısa ve okunabilir olmalıdır. Değişimi isimler anlaşılmasını kolaylaştırmak için seçilmelidir.

Bu örnek, bu önerileri açıkça ihlal etmektedir.


3
Yoksa muhtemelen u == 0 duracak ...?
Thomas

2
Hayır; C ++ 'da int'ten boole içine örtük bir dönüştürme vardır ve 0 false değerine dönüştürülür. U-- == 0 olduğunda döngü sonlanır. C # 'da, böyle bir örtük dönüşüm yoktur, bu yüzden açıkça u-- == 0 demeniz gerekir.
Chris

48
Bu çok basit bir nedenden dolayı korkunç bir koddur; okuduğunuzda kolayca anlayamadınız. "Zeki", "hack"; işi yapan ancak anlayışa meydan okuyan bir yapı oluşturmak için, kodlama yapılarının bir kombinasyonunu ve sahne arkasında nasıl çalıştıklarını bilmekten yararlanır, çünkü dil yazarlarının öngördüğü ve çoğu kişiye iletildiği şeklinde değildir dil kullanıcıları.
KeithS

4
Mükemmel cevap. + 1'leyeceğim ... değilse "Bu çok kötü bir kod." ifadesi;)
Sandman4 31:07

14
Neden bu kadar çok insan u - sadece eksik (C ++ kapalı) nedeniyle gerçekten kötü bir kod olduğunu düşünüyor görünmüyor ! = 0 . Şüphesiz, kodla çalışan herkes 0 = yanlış, diğer her değer = doğru olduğunun farkında olacaktır . Öncesi / sonrası artan ilgili karışıklıktan dolayı çok daha fazla kapsam var u , ya da insanlar belki varsayarak () u = b.size olacak hep önce yürütmek (v = b.back) (benim anlayış yürütme dizisi var tanımlanmamış olup, ama düzeltilmeye devam ediyorum).
FumbleFingers

23

Bu, döngünüzün C # formu olacaktır.

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

Size () ve back () için eşdeğerini değiştirin.

Yaptığı şey, listeyi tersine çevirir ve bir dizide saklar. Ancak C # 'da bunun için doğrudan sistem tanımlı fonksiyona sahibiz. Yani bu döngüyü de yazmanıza gerek yok.

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

1
v = b.back();intializer için dışarı alarak sadece çalışma şeklini değiştirmek değil mi, çünkü v = p[v]tarafından geçersiz kılındı
João Portela

v = p [v], bu satır sonunda yürütüleceğinden nihai sonuç üzerinde herhangi bir etkisi olmayacaktır. Ve bundan sonra döngüde v belirtilmez. Bu çizgi sadece döngü c ++ 'dan C #' ye nasıl dönüştürüldüğünü gösterir.
Narendra

1
c ++ kodunda v = b.back();yinelemeler başlamadan önce bir kez v = p[v]yürütüldü ve her yinelemenin başında yürütüldü. Bu C # sürümü v = p[v]hala her yinelemenin başında yürütülür, ancak v = b.back();hemen ardından yürütülür vve bir sonraki komutun değerini değiştirir b[u] = v;. (belki de okuduktan sonra soru düzenlenmiştir)
João Portela

5
@Rain Sorun v = b.back(). Bunu yerine ilki her döngü tekrarında infaz var - biz bilmiyoruz back()mu (herhangi bir yan etkileri vardır o iç sunumunu değiştirir mi? bBu döngü böylece?) Değil de birine denk soru.
Izkata

1
@ Tam olarak, kendinize daha yakından bakın. Başlatma adımı, sadece döngü başlamadan önce bir kez olur değil başında her yineleme . Üstündeki v = b.back()döngünün dışına taşındıysa kodunuz doğru olurdu . (Ayrıca, birisine yanıt vermeye çalışıyorsanız @, adının önünde kullanın , bu yüzden bir bildirim
alırız


14

Durum , azaltılmadan u--önceki değeri olan sonucudur u.

C ve C ++ ile bir int olup örtük bir yaparak bool dönüştürülebilir != 0karşılaştırma (0 false, her şey olduğu true).

b.back()bir kaptaki son öğedir b[b.size() - 1], yani size() != 0.


11

C'de sıfır olmayan her şey truedöngü sonu koşulu veya koşullu bir ifade gibi "boole" bağlamındadır. C # 'o onay açık yapmak zorunda: u-- != 0.


Bu OP meselesi değil. Terminal durumunun değerlendirmesini soruyor (yani bu durumda 'u--').
ApplePie

6

Başkaları tarafından belirtildiği gibi, C ++ 'ın boole'ye dolaylı döküm yapması koşulun, u--değer sıfırdan farklı olduğunda geçerli olacağı anlamına gelir .

Eklemekte fayda var, "şartlı olanı nerede" diye sormakta yanlış bir varsayım var. Hem C ++ hem de C # 'da (ve benzer şekilde sözdizimi yapılan dillerde) boş bir koşulunuz olabilir. Bu durumda her zaman döngü sonsuza devam veya bunu, doğru olarak değerlendirilir başka koşul çıkar kadar o (via return, breakya throw).

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

Aslında, for ifadesinin herhangi bir kısmı dışarıda bırakılabilir, bu durumda sadece gerçekleştirilmez.

Genel olarak for(A; B; C){D}veya for(A; B; C)D;olur:

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

A, B, C veya D'nin herhangi biri veya daha fazlası dışarıda bırakılabilir.

Bunun sonucu olarak, for(;;) sonsuz döngüler için bir iyilik . Çünkü while(true)daha popüler olsa da, bunu "hakikat gerçek oluncaya kadar" olarak okuyorum, bu da benim okumamla for(;;) "sonsuza dek" olarak biraz kıyamet gibi geliyor .

Bu bir zevk meselesi, ama dünyada hoşuna giden tek kişi olmadığım için for(;;)ne anlama geldiğini bilmeye değer.


4

tüm cevaplar doğru: -

for loop aşağıdaki gibi çeşitli şekillerde kullanılabilir:

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;

Yukarıdaki kodu, uve vile başlatılır b.size()ve b.back().

Koşul her kontrol edildiğinde azalma ifadesini de çalıştırır u--.

forNe zaman döngü çıkılacak uhale gelecektir 0.


3

C # 'da karşılaşılan hata şüphe giderir. For-loop bir

YANLIŞ

sonlandırma koşulu. Ve bildiğimiz gibi,

(BOOL) YANLIŞ = (int) 0

ancak C # bunu C ++ 'dan farklı olarak kendi başına işleyemez. Yani aradığınız koşul

dimi

ama açıkça C # 'daki koşulu

u--! = 0

veya

u--> 0

Ama yine de bu tür kodlama uygulamalarından kaçınmaya çalışın.

döngü sırasında

Yukarıda yanıt olarak belirtilen,

döngü için.


@Downvoter: Downvoting, çözümden memnun olmadığınız sürece iyidir, ancak aynı zamanda, nedenleri belirtmek için zaman ayırın, böylece cevaplar geliştirilebilir.
Mart'ta Abhineet

3

C / C ++ 'a alışkınsanız, bu kodun okunması o kadar zor değildir, ancak oldukça tik ve kod büyük değildir. Öyleyse, her şeyden daha Cism olan parçaları açıklayayım. İlk önce bir C for loop'un genel sözdizimi şöyle görünür:

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

Başlatma kodu bir kez çalıştırılır. Daha sonra durum her döngüden önce test edilir ve son olarak her döngüden sonra artış çağrılır. Yani, örneğinizde durumunu--

Neden u--C # 'da değil C' de koşul olarak çalışır? Çünkü C bir çok şeyi dolaylı olarak bools'a dönüştürür ve belaya neden olabilir. Bir sayı için sıfır olmayan her şey doğrudur ve sıfır yanlıştır. Bu yüzden b.size () - 1'den 0'a kadar geri sayılacaktır. Durumda yan etkiye sahip olmak biraz sinir bozucu ve çok fazla C olsa da for döngüsünün artış kısmına koymak tercih edilir. kodu bunu yapar. Eğer yazıyor olsaydım bunu daha çok yaparım:

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

Bunun nedeni, en azından benim için daha açık. For döngüsünün her parçası bu işi yapar ve başka hiçbir şey yapmaz. Orijinal kodda koşul değişkeni değiştiriyordu. Artış kısmı, kod bloğunda vb. Olması gereken bir şey yapıyordu.

Virgül operatörü de sizi bir döngü için atıyor olabilir. C'de x=1,y=2derleyici söz konusu olduğunda tek bir ifadeye benziyor ve başlatma koduna uyuyor. Sadece parçaların her birini değerlendirir ve sonuncunun değerini döndürür. Yani mesela:

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

çıktı 2.


Yeniden yazmanızla ilgili sorun, b.size () imzasızsa, çok uzun bir süre yinelemesidir. (Ayrıca, noktalı virgül eksik.) Ama en azından diğer cevapların ve yorumların çoğunun yaptığı ve sadece daha kolay olan saçma ayrıntılı yeniden yazmalara övünen "Dick ve Jane" iyi İngilizce "yaklaşımını benimsemediniz. neofitler ve diğer vasıfsız programcılar tarafından okunmak.
Jim Balter
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.