Kare basamakları toplarken negatif sayıları veya sıfırları açıkça işlemem gerekir mi?


220

Geçenlerde sınıfımda bir test yaptım. Sorunlardan biri şuydu:

Bir n sayısı verildiğinde , C / C ++ 'da karenin içindeki sayının toplamını döndüren bir işlev yazın . (Aşağıdakiler önemlidir). Aralığı içinde n olduğu [- (10 ^ 7), 10 ^ 7]. Örnek: n = 123 ise, işleviniz 14 döndürmelidir (1 ^ 2 + 2 ^ 2 + 3 ^ 2 = 14).

Yazdığım işlev budur:

int sum_of_digits_squared(int n) 
{
    int s = 0, c;

    while (n) {
        c = n % 10;
        s += (c * c);
        n /= 10;
    }

    return s;
}

Bana doğru baktı. Şimdi test geri döndü ve anladığım bir nedenden dolayı öğretmenin bana tüm puanları vermediğini gördüm. Ona göre, işlevimin tamamlanması için, aşağıdaki ayrıntıyı eklemeliydim:

int sum_of_digits_squared(int n) 
 {
    int s = 0, c;

    if (n == 0) {      //
        return 0;      //
    }                  //
                       // THIS APPARENTLY SHOULD'VE 
    if (n < 0) {       // BEEN IN THE FUNCTION FOR IT
        n = n * (-1);  // TO BE CORRECT
    }                  //

    while (n) {
        c = n % 10;
        s += (c * c);
        n /= 10;
    }

    return s;
}

Bunun argümanı, n sayısının [- (10 ^ 7), 10 ^ 7] aralığında olmasıdır, bu nedenle negatif bir sayı olabilir. Fakat fonksiyonun kendi versiyonunun nerede başarısız olduğunu görmüyorum. Eğer doğru anlamak, anlamı while(n)ise while(n != 0), değil while (n > 0) , bu yüzden fonksiyonun benim sürümünde sayı n döngüye girmesine başarısız olmaz. Aynı şekilde çalışır.

Sonra, bilgisayarımdaki fonksiyonun her iki versiyonunu da denedim ve denediğim tüm örnekler için tam olarak aynı cevapları aldım. Yani, sum_of_digits_squared(-123)eşittir sum_of_digits_squared(123)(yine, eşittir 14) (görünüşte eklemem gereken detay olmadan bile). Ekranda (en azdan önemi en büyüklerine) sayısının rakamlarını yazdırmaya çalışırsanız Nitekim, içinde 123söz alıyorum 3 2 1ve-123 söz alıyorum -3 -2 -1(tür ilginç aslında olan). Ancak bu problemde rakamları kareye aldığımızdan önemli değil.

Peki, kim yanlış?

EDIT : Benim kötü, belirtmeyi unuttum ve önemli olduğunu bilmiyordum. Sınıfımızda ve testlerde kullanılan C versiyonu C99 veya daha yeni olmalıdır . Bu yüzden (yorumları okuyarak) sürümümün herhangi bir şekilde doğru cevabı alacağını tahmin ediyorum.


120
n = n * (-1)yazmak için gülünç bir yol n = -n; Sadece bir akademisyen bunu bile düşünebilirdi. Gereksiz parantezleri ekleyelim.
user207421

32
Belirli bir uygulamanın spesifikasyona uygun olup olmadığını kontrol etmek için bir dizi birim test yazın. Bir kod parçasında (işlevsel) bir sorun varsa, belirli bir girdi verildiğinde yanlış sonucu gösteren bir test yazmak mümkün olmalıdır.
Carl

94
"Kareli sayının rakamlarının toplamının" üç (3) tamamen farklı şekilde yorumlanabileceğini ilginç buluyorum. (Sayı 123 ise, olası yorumlar 18, 14 ve 36'yı verir.)
Andreas Rejbrand

23
@ilkkachu: "kare sayının rakamlarının toplamı". Peki, "kare sayısı" açıkça 123 ^ 2 = 15129, yani "kare sayısı arasındaki rakamların toplamı" "1 + 5 + 1 + 2 + 9" olan "15129 rakamlarının toplamı" dır. = 18.
Andreas Rejbrand

15
n = n * (-1)? Wut ??? Prof'nizin aradığı şudur: `n = -n '. C dili tek yönlü eksi operatörüne sahiptir.
Kaz

Yanıtlar:


245

Yorumlarda yer alan bir tartışmayı özetleme:

  • Önceden test etmek için iyi bir neden yoktur n == 0. while(n)Test mükemmel bu davayı idare edecek.
  • Muhtemelen öğretmeniniz, %negatif işlenenlerin sonucunun farklı tanımlandığı daha önceki zamanlara alışkındır. , Sonucu (bir PDP-11, Dennis Ritchie aslen geliştirilen C erken, özellikle, Unix dahil) bazı eski sistemlerde a % bolduğu zaman aralığında [0 .. b-1]-123% 10 böyle bir sistemin 7. olduğunu, test anlamına gelen önceden n < 0gerekli olacaktır.

Ancak ikinci mermi sadece daha önceki zamanlar için geçerlidir. Hem C hem de C ++ standartlarının mevcut sürümlerinde, tamsayı bölümü 0'a doğru kesilecek şekilde tanımlanır, bu nedenle n % 10negatif nolduğunda bile (muhtemelen negatif) son basamağı vermeniz garanti edilir n.

Yani "Ne anlama geliyor while(n)?" Sorusunun cevabı. olduğu "birbirinin aynı while(n != 0)" ve cevabı "Will negatif düzgün bu kod çalışması yanı pozitif olarak n?" olduğu "Evet, herhangi bir modern altında, derleyici Standartları-uyumlu." "O halde eğitmen neden onu işaretledi?" Sorusunun cevabı. muhtemelen 1999'da C'ye ve 2010'da C ++ 'a olan önemli bir dil yeniden tanımlamasının farkında olmamalarıdır.


39
"N == 0 için önceden test etmek için iyi bir neden yok" - teknik olarak, bu doğru. Ancak bir öğretim ortamında bir profesörden bahsettiğimiz göz önüne alındığında, kısalık konusundaki netliği bizden çok daha fazla takdir edebilirler. Fazladan test eklenmesi, n == 0en azından herhangi bir okuyucu için bu durumda olanları hemen ve tamamen açık hale getirir. Onsuz, okuyucu, döngünün gerçekten atlandığını ve varsayılan sdöndürülen değerin doğru olduğunu tatmin etmelidir .
ilkkachu

22
Ayrıca, profesör, öğrencinin farkında olduğunu ve işlevin neden ve nasıl sıfır girişiyle davrandığını (yani kazara doğru değeri döndürmediğini) düşündüğünü bilmek isteyebilir . Bu durumda ne olacağını, döngüün sıfır kez çalışabileceğini vb. Fark etmeyen öğrencilerle tanışmış olabilirler. Bir öğretim ortamıyla uğraşırken, herhangi bir varsayım ve köşe vakası hakkında daha net olmak en iyisidir. ..
ilkkachu

36
@ilkkachu Durum buysa, öğretmen doğru çalışması için böyle bir test yapılmasını gerektiren bir görev vermelidir.
Klutt

38
@ilkkachu Pekala, genel durumdaki fikrinizi anlıyorum, çünkü kısalık konusundaki netliği kesinlikle takdir ediyorum - ve neredeyse her zaman, sadece pedagojik bir ortamda değil. Söyledi ile Ama, bazen kısalık olduğu netlik ve genel durum hem işlemek için ana kodu için ayarlayabilir eğer ve kenar durumlar için özel durumlar ile kodunu (ve kapsama analizi) karmaşık hale getirmeden, kenar durumda (lar) , bu çok güzel bir şey! Bence bu başlangıç ​​seviyesinde bile takdir edilecek bir şey.
Steve Summit

49
@ilkkachu Bu argümanla mutlaka n = 1 için testler eklemelisiniz. Özel bir şey yok n=0. Gereksiz dalları ve komplikasyonları tanıtmak kodu daha kolay hale getirmez, zorlaştırır, çünkü şimdi sadece genel algoritmanın doğru olduğunu göstermek zorunda değilsiniz, ayrıca tüm özel durumlar hakkında ayrı olarak düşünmeniz gerekir.
Voo

107

Kodunuz gayet iyi

Kesinlikle haklısınız ve öğretmeniniz yanlış. Sonucu hiç etkilemediğinden, bu ekstra karmaşıklığı eklemek için kesinlikle hiçbir neden yoktur. Hatta bir hata bile veriyor. (Aşağıya bakınız)

İlk olarak, eğer nsıfır olup olmadığını kontrol etmek kesinlikle gereksizdir ve bunun gerçekleştirilmesi çok kolaydır. Dürüst olmak gerekirse, aslında öğretmenlerinizin bu konuda itirazları olup olmadığını sorgularım. Ama sanırım herkes zaman zaman bir beyin osuruğuna sahip olabilir. Ancak, bunun while(n)değiştirilmesi gerektiğini düşünüyoruz, while(n != 0)çünkü ekstra bir çizgiye bile mal olmadan biraz ekstra netlik katıyor. Yine de küçük bir şey.

İkincisi biraz daha anlaşılır, ama yine de yanlış.

Bu nedir C11 standart 6.5.5.p6 diyor ki:

A / b bölümü gösterilebiliyorsa, (a / b) * b + a% b ifadesi a; aksi takdirde, hem a / b hem de% b davranışları tanımlanmamıştır.

Dipnot şunu söylüyor:

Buna genellikle "sıfıra doğru kesme" denir.

Sıfır aracına doğru Kesme için mutlak değer olduğunu a/bmutlak değerine eşit (-a)/bherkes için ave bdönüş araçlarında kodunuzu mükemmel ince olduğunu.

Modulo kolay matematiktir, ancak sezgisel olabilir

Ancak, öğretmeninizin dikkatli olmanız gerektiği bir noktası var, çünkü sonucu karelemeniz aslında burada çok önemlidir. a%bYukarıdaki tanımlamaya göre hesaplamak kolay matematiktir, ancak sezginize aykırı olabilir. Çarpma ve bölme için, işlenenler eşittir işaretine sahipse sonuç pozitiftir. Ancak modulo söz konusu olduğunda, sonuç ilk işlenenle aynı işarete sahiptir . İkinci işlenen işareti hiç etkilemez. Mesela, 7%3==1ama (-7)%(-3)==(-1).

İşte bunu gösteren bir pasaj:

$ cat > main.c 
#include <stdio.h>

void f(int a, int b) 
{
    printf("a: %2d b: %2d a/b: %2d a\%b: %2d (a%b)^2: %2d (a/b)*b+a%b==a: %5s\n",
           a, b ,a/b, a%b, (a%b)*(a%b), (a/b)*b+a%b == a ? "true" : "false");
}

int main(void)
{
    int a=7, b=3;
    f(a,b);
    f(-a,b);
    f(a,-b);
    f(-a,-b);
}

$ gcc main.c -Wall -Wextra -pedantic -std=c99

$ ./a.out
a:  7 b:  3 a/b:  2 a%b:  1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a: -7 b:  3 a/b: -2 a%b: -1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a:  7 b: -3 a/b: -2 a%b:  1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a: -7 b: -3 a/b:  2 a%b: -1 (a%b)^2:  1 (a/b)*b+a%b==a:  true

Yani, ironik bir şekilde, öğretmeniniz yanlış olduğunu kanıtladı.

Öğretmeninizin kodu hatalı

Evet, aslında öyle. Eğer giriş INT_MINVE ise mimari ikinin tamamlayıcısı VE işaret bitinin 1 olduğu ve tüm değer bitlerinin 0 olduğu bit deseni ise bir tuzak DEĞİLDİR (ikisinin tuzak değerleri olmadan tamamlayıcısını kullanmak çok yaygındır), o zaman öğretmeninizin kodu tanımlanmamış davranış verecektir hatta n = n * (-1). Kodunuz - eğer çok azsa - ondan daha iyidir. Ve kodu gereksiz karmaşık hale getirerek ve kesinlikle sıfır değer kazanarak küçük bir hata getirmeyi düşününce, kodunuzun ÇOK daha iyi olduğunu söyleyebilirim.

Başka bir deyişle, INT_MIN = -32768 (sonuçta ortaya çıkan işlev <-32768 veya> 32767 olan bir girdi alamıyor olsa da) derlemelerde, geçerli -32768 girdisi tanımlanmamış davranışa neden olur, çünkü - (- 32768i16) 16 bit tam sayı olarak ifade edilemez. (Aslında, -32768 muhtemelen yanlış bir sonuca neden olmaz, çünkü - (- 32768i16) genellikle -32768i16 olarak değerlendirilir ve programınız negatif sayıları doğru şekilde işler.) (SHRT_MIN, derleyiciye bağlı olarak -32768 veya -32767 olabilir.)

Ancak öğretmeniniz açıkça n[-10 ^ 7; 10 ^ 7]. 16 bitlik bir tam sayı çok küçük; en azından 32 bitlik bir tam sayı kullanmanız gerekir. Kullanmak int, kodunu güvenli hale getirebilir, ancak int32 bitlik bir tam sayı olması gerekmez. 16 bit mimari için derlerseniz, kod snippet'lerinizin her ikisi de kusurludur. Ancak kodunuz hala çok daha iyi çünkü bu senaryo, INT_MINyukarıda belirtilen sürümü ile birlikte hatayı yeniden ortaya koyuyor. Bunu önlemek için, yazabilir longyerine intya mimarisine bir 32 bitlik tamsayı olan. A'nın long, [-2147483647 aralığında herhangi bir değeri tutabileceği garanti edilmektedir; 2147483647]. C11 Standart 5.2.4.2.1 LONG_MIN genellikle-2147483648ancak maksimum izin değeri (evet, maksimum, negatif bir sayıdır)LONG_MINolduğunu 2147483647.

Kodunuzda ne gibi değişiklikler yapardım?

Kodunuz olduğu gibi iyidir, bu yüzden bunlar gerçekten şikayet değildir. Kodunuz hakkında gerçekten bir şey söylemem gerekiyorsa, bunu biraz daha net hale getirebilecek bazı küçük şeyler var.

  • Değişkenlerin isimleri biraz daha iyi olabilir, ancak anlaşılması kolay kısa bir işlevdir, bu yüzden önemli değildir.
  • Koşulu olarak olarak ndeğiştirebilirsiniz n!=0. Anlamsal olarak,% 100 eşdeğerdir, ancak biraz daha açık hale getirir.
  • c(Yeniden adlandırdığım digit) bildirimini while döngüsü içinde yalnızca orada kullanıldığından taşıyın .
  • longGirdi kümesinin tamamını işleyebilmesini sağlamak için bağımsız değişken türünü değiştirin .
int sum_of_digits_squared(long n) 
{
    long sum = 0;

    while (n != 0) {
        int digit = n % 10;
        sum += (digit * digit);
        n /= 10;
    }

    return sum;
}

Aslında, bu biraz yanıltıcı olabilir, çünkü - yukarıda belirtildiği gibi - değişken digitnegatif bir değer alabilir, ancak bir rakam kendi başına asla pozitif veya negatif değildir. Bu konuda birkaç yol var, ama bu gerçekten çirkin, ve ben böyle küçük detaylar umurumda değil. Özellikle son basamağın ayrı işlevi çok ileri gidiyor. İronik olarak, bu öğretmenlerin kodlarının aslında çözdüğü şeylerden biridir.

  • Değişim sum += (digit * digit)için sum += ((n%10)*(n%10))ve değişken atlamak digittamamen.
  • digitNegatif ise işaretini değiştirin . Ama sadece bir değişken mantıklı yapmak için kodu daha karmaşık hale karşı şiddetle tavsiye ediyorum. Bu ÇOK güçlü bir kod kokusu.
  • Son basamağı ayıklayan ayrı bir işlev oluşturun. int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }Bu işlevi başka bir yerde kullanmak istiyorsanız bu yararlıdır.
  • Sadece cbaşlangıçta yaptığınız gibi adlandırın. Bu değişken adı yararlı bilgi vermez, ancak diğer yandan da yanıltıcı değildir.

Ama dürüst olmak gerekirse, bu noktada daha önemli çalışmalara geçmelisiniz. :)


19
Giriş ise INT_MINve mimari ikisinin tamamlayıcısını kullanıyorsa (ki bu çok yaygındır), öğretmen kodunuz tanımlanmamış davranış verecektir. Ahh. Bu bir iz bırakacak. ;-)
Andrew Henle

5
(a/b)*b + a%b ≡ aOP kodunun yanı sıra /sıfıra doğru yuvarlandığı ve buna bağlı olduğu da belirtilmelidir (-c)*(-c) ≡ c*c. Tüm bunları garanti eden standartlara rağmen ekstra kontrollerin haklı olduğu söylenebilir, çünkü yeterince açık değildir. (Tabii ki, ilgili standart bölümleri bağlayan bir yorum olması gerektiğini eşit derecede iyi iddia edebiliriz, ancak stil yönergeleri değişir.)
leftaroundabout

7
@MartinRosenau "Belki" diyorsunuz. Bunun gerçekten gerçekleştiğinden mi yoksa standart veya bir şey tarafından izin verildiğinden mi emin misiniz yoksa sadece spekülasyon yapıyor musunuz?
Klutt

6
@MartinRosenau: Tamam, ama bu anahtarları kullanmak artık C değil. GCC / clang, farkında olduğum herhangi bir ISA'da tamsayı bölmeyi bozan anahtarlara sahip değil. İşaret bitini görmezden gelmek, sabit bölücüler için normal çarpma tersini kullanarak bir hız verebilir. C böylece (Ama bir donanım bölümü talimat var ben aşinayım tüm ISA'lar sıfıra doğru kısaltılıyor, o C99 yöntemi uygulayan %ve /operatörler sadece derlemek olabilir idivx86 veya, sdivARM ya da her türlü. Yine de, çok ilgisiz olduğunu derleme zamanı sabit bölücüler için daha hızlı kod-gen)
Peter Cordes

5
@TonyK AFIK, genellikle bu şekilde çözülür, ancak standarda göre UB'dir.
Klutt

20

Senin versiyonunu ya da öğretmenini tamamen sevmiyorum. Öğretmeninizin sürümü, doğru bir şekilde belirttiğiniz ekstra testleri gereksiz yapar. C'nin mod operatörü uygun bir matematiksel mod değildir: negatif bir sayı mod 10 negatif bir sonuç üretir (uygun matematiksel modül her zaman negatif değildir). Ama yine de karesini aldığınız için hiçbir fark yok.

Ama bu açık olmaktan çok uzak, bu yüzden kodunuza öğretmeninizin kontrollerini değil, neden işe yaradığını açıklayan büyük bir yorum ekleyeceğim. Örneğin:

/ * NOT: Bu modül negatif değerler için çalışır, çünkü modül kareler alır * /


9
C'ler en %iyi olarak kalan olarak adlandırılır , çünkü işaretli türler için bile.
Peter Cordes

15
Kareleme önemli, ama bence bu bariz kısım. -7 % 10 -7
Belirtilmesi

5
“Uygun matematiksel modül” hiçbir şey ifade etmez. Doğru terim “Öklid moduloudur” (son eke dikkat edin!) Ve aslında C'nin %operatörü bu değildir.
Jan Hudec

Bu cevabı beğendim çünkü modulo'u yorumlamanın birden fazla yolu sorusunu çözüyor. Böyle bir şeyi asla şansa / yoruma bırakmayın. Bu kod golf değil.
Harper - Monica'yı

1
"uygun matematiksel modül her zaman negatif değildir" - Pek değil. Bir modulo işleminin sonucu bir denklik sınıfıdır , ancak sonucu o sınıfa ait en küçük negatif olmayan sayı olarak değerlendirmek yaygındır.
Klutt

10

NOT: Bu yanıtı yazarken C'yi kullandığınızı açıkladınız. Cevabımın çoğunluğu C ++ ile ilgili. Ancak, başlığınız hala C ++ olduğundan ve soru hala C ++ olarak etiketlendiğinden, bu durumun diğer insanlar için hala yararlı olması durumunda yine de cevap vermeyi seçtim, özellikle de şimdiye kadar gördüğüm cevapların çoğu çoğunlukla tatmin edici olmadığından.

Modern C ++ 'da (Not: C'nin bunun nerede durduğunu gerçekten bilmiyorum), profesörünüz her iki konuda da yanlış görünüyor.

Birincisi, işte bu kısım:

if (n == 0) {
        return 0;
}

C ++ 'da, bu temel olarak aynı şeydir :

if (!n) {
        return 0;
}

Bu, zamanınızın böyle bir şeye eşdeğer olduğu anlamına gelir:

while(n != 0) {
    // some implementation
}

Bu, eğer süre yine de yürütülmezse, sadece çıkmanız gerektiğinden, gerçekten burada olmanız için bir neden yoktur, çünkü döngüden sonra ve eğer zaten yaptığınız şey yine de eşdeğerdir. Her ne kadar bunun farklı olduğunu söylememe rağmen, eğer buna sahip olmanız gerekir.

Yani gerçekten, bu if ifadesi yanılmıyorsam özellikle yararlı olmaz.

İkinci bölüm işlerin kıllı olduğu yer:

if (n < 0) {
    n = n * (-1);
}  

Sorunun kalbi, negatif bir sayının modülünün çıktısının çıktısıdır.

Modern C ++ 'da, bu çoğunlukla iyi tanımlanmış gibi görünüyor :

İkili / operatör, bölümü verir ve% ikili% operatörü, ilk ifadenin ikinciye bölünmesinden kalanını verir. / Veya% ifadesinin ikinci işleneni sıfırsa, davranış tanımsızdır. İntegral işlenenler için / operatörü herhangi bir fraksiyonel parça atılarak cebirsel bölüm verir; a / b bölümü sonuç türünde gösterilebiliyorsa, (a / b) * b + a% b eşittir a.

Ve sonra:

Her iki işlenen de negatif değilse, geri kalan negatif değildir; değilse, kalanın işareti uygulama tarafından tanımlanır.

Alıntılanan cevabın posterinin doğru bir şekilde işaret ettiği gibi, bu denklemin önemli kısmı tam burada:

(a / b) * b + a% b

Vakanıza bir örnek aldığınızda, şöyle bir şey elde edersiniz:

-13/ 10 = -1 (integer truncation)
-1 * 10 = -10
-13 - (-10) = -13 + 10 = -3 

Tek yakalama şu son satırdır:

Her iki işlenen de negatif değilse, geri kalan negatif değildir; değilse, kalanın işareti uygulama tarafından tanımlanır.

Bu, böyle bir durumda, yalnızca işaretin uygulama tanımlı olduğu anlamına gelir . Bu sizin durumunuzda bir sorun olmamalı çünkü zaten bu değeri kareliyorsunuz.

Bununla birlikte, bunun C ++ veya C99'un önceki sürümleri için geçerli olmadığını unutmayın. Profesörünüz bunu kullanıyorsa, nedeni bu olabilir.


EDIT: Hayır, yanılıyorum. Bu , C99 veya üstü için de geçerli gibi görünüyor :

C99, a / b temsili olduğunda şunları gerektirir:

(a / b) * b + a% b a eşittir

Ve başka bir yer :

Tamsayılar bölündüğünde ve bölünme hatalı olduğunda, her iki işlenen de pozitifse / işlecinin sonucu cebirsel bölümden daha küçük en büyük tamsayıdır ve% işlecinin sonucu pozitiftir. Her iki işlenen de negatifse, / işlecinin sonucunun,% işlecinin sonucunun işareti gibi, cebirsel bölümden daha küçük en büyük tamsayı mı yoksa cebirsel bölümden daha büyük en küçük tamsayı mı uygulama tanımlıdır. A / b bölümü gösterilebilirse, (a / b) * b + a% b ifadesi a değerine eşit olacaktır.

ANSI C veya ISO C, -5% 10'un ne olması gerektiğini belirtir mi?

Yani evet. C99'da bile, bu sizi etkilemiyor gibi görünmüyor. Denklem aynıdır.


1
Alıntıladığınız bölümler bu yanıtı desteklemiyor. “kalanın işareti uygulama tarafından tanımlanmıştır” (-1)%10üretebileceği anlamına gelmez -1veya 1; bu üretebileceği -1veya 9ikinci durumda (-1)/10üreteceği -1ve OP kodunun asla sona ermeyeceği anlamına gelir.
stewbasic

Bunun için bir kaynağa işaret edebilir misiniz? (-1) / 10'un -1 olduğuna inanarak çok zorlandım. Bu da 0 olmalıdır. Ayrıca, (-1)% 10 = 9 geçerli denklemi ihlal ediyor gibi görünüyor.
Chipster

1
@Chipster ile başlayın (a/b)*b + a%b == a, sonra bırakın a=-1; b=10, verin (-1/10)*10 + (-1)%10 == -1. Şimdi, eğer -1/10gerçekten aşağı yuvarlanırsa (-inf'ye doğru), o zaman var (-1/10)*10 == -10ve (-1)%10 == 9ilk denklemin eşleşmesi gerekiyor. Gibi diğer cevaplar devlet, geçerli standart (lar) içinde nasıl çalıştığını bu değil, ama buna işin eskiden nasıl bu. Bu gibi kalanının işareti, ama nasıl bölünme mermi ve ne kalanı daha sonra ilgili değil sahip denklemi tatmin olmak.
ilkkachu

1
@Chipster Kaynak alıntıladığınız parçacıklardır. Unutmayın ki (-1)*10+9=-1, seçim (-1)/10=-1ve (-1)%10=9denklemi ihlal etmez. Öte yandan seçim (-1)%10=1, nasıl (-1)/10seçilirse seçilsin yönetim denklemini karşılayamaz ; tam sayı orada hiçbir qşekilde q*10+1=-1.
stewbasic

8

Diğerlerinin de belirttiği gibi, n == 0 için özel tedavi saçmalıktır, çünkü her ciddi C programcısı için "while (n)" işini yapar.

N <0 için davranış o kadar açık değil, bu yüzden bu 2 kod satırını görmeyi tercih ederim:

if (n < 0) 
    n = -n;

veya en azından bir yorum:

// don't worry, works for n < 0 as well

Dürüst olmak gerekirse, ne zaman n'nin negatif olabileceğini düşünmeye başladınız? Kodu yazarken veya öğretmeninizin sözlerini okurken?


1
N'nin negatif olup olmadığına bakılmaksızın, N kare pozitif olacaktır. Peki, işareti neden ilk etapta kaldırıyorsunuz? -3 * -3 = 9; 3 * 3 = 9. Ya da öğrendiğimden bu yana 30 yıl içinde matematik değişti mi?
Merovex

2
@CB Adil olmak gerekirse, testi yazarken n'nin negatif olabileceğini fark etmedim, ancak geri döndüğünde sayı negatif olsa bile while döngüsünün atlanmayacağını hissettim. Bilgisayarımda bazı testler yaptım ve şüphem doğrulandı. Bundan sonra bu soruyu gönderdim. Yani hayır, kodu yazarken çok derin düşünmemiştim.
user010517720

5

Bu bana başarısız olduğum bir ödevi hatırlatıyor

90'larda geri dönüş. Öğretim görevlisi döngüler hakkında filizlenmişti ve uzun öykü kısa, ödevimiz verilen herhangi bir tam sayı> 0 için rakam sayısını döndürecek bir işlev yazmaktı.

Yani, örneğin, hane sayısı 321olurdu3 .

Ödevin sadece basamak sayısını döndüren bir işlev yazdığı söylense de, beklenti, dersin kapsadığı gibi, onu elde edene kadar 10'a kadar bölen bir döngü kullanmamızdır. .

Ancak döngüler kullanmak açıkça belirtilmedi, ben: took the log, stripped away the decimals, added 1ve daha sonra tüm sınıfın önünde kuzulandı.

Mesele şu ki, ödevin amacı, dersler sırasında neler öğrendiğimizi anlamaktı . Aldığım dersten bilgisayar öğretmeninin biraz pislik olduğunu öğrendim (ama belki bir planla pislik?)


Durumunuzda:

C / C ++ 'da karenin içindeki sayının toplamını döndüren bir işlev yazın

Kesinlikle iki cevap verirdim:

  • doğru cevap (önce sayının karesini alma) ve
  • sadece onu mutlu etmek için örneğe uygun yanlış cevap ;-)

5
Ve ayrıca üçüncü bir rakam da rakamların toplamını mı?
kriss

@kriss - evet, ben o kadar akıllı değilim :-(
SlowLearner

1
Öğrenci zamanımda da çok belirsiz değerlendirmelerden payımı aldım. Bir öğretmen biraz manipülasyon kütüphanesi istedi, ancak kodumdan şaşırdı ve çalışmadığını söyledi. Ona, görevinde hiç endianlığı tanımlamadığını ve diğer seçimi yaparken alt biti 0 bit olarak seçtiğini belirtmeliydim. Can sıkıcı olan tek şey, farkın bana söylemeden nerede olduğunu anlayabilmiş olmasıydı.
kriss

1

Genellikle atamalarda, kodun çalışması nedeniyle tüm işaretler verilmez. Ayrıca bir çözümü kolay okunur, verimli ve zarif hale getirmek için işaretler de alırsınız. Bunlar her zaman karşılıklı olarak münhasır değildir.

Ben yeterince strees olamaz "anlamlı değişken adları kullanın" dir .

Örneğinizde çok fazla fark yaratmıyor, ancak milyonlarca kod okunabilirliği olan bir proje üzerinde çalışıyorsanız çok önemli hale geliyor.

C kodu ile görme eğiliminde olduğum başka bir şey, insanlar akıllı görünmeye çalışıyor. Süre (n! = 0) kullanmak yerine herkese yazarak ne kadar zeki olduğumu göstereceğim (n) çünkü aynı anlama geliyor. Peki sahip olduğunuz derleyicide var ama önerdiğiniz gibi öğretmenin eski sürümü aynı şekilde uygulamamıştır.

Yaygın bir örnek, bir dizideki bir dizine aynı anda artırılırken başvuruda bulunulmasıdır; Sayılar [i ++] = iPrime;

Şimdi, kod üzerinde çalışan bir sonraki programcı ödev önce veya sonra ben artmış olup olmadığını bilmek zorunda, sadece biri göstermek olabilir.

Bir megabayt disk alanı, bir rulo tuvalet kağıdından daha ucuzdur, yerden tasarruf etmeye çalışmak yerine netlik kazanır, programcı arkadaşlarınız daha mutlu olur.


2
C'de sadece birkaç kez programladım ve ++ideğerlendirme öncesi i++artışları ve sonrasındaki artışları biliyorum . while(n)aynı zamanda ortak bir dil özelliğidir. Böyle bir mantığa dayanarak böyle kod bol gördüm if (foo == TRUE). Yine de katılıyorum: değişken isimleri.
alan ocallaghan

3
Genelde bu kötü bir tavsiye değildir, ancak savunma programlama amacıyla temel dil özelliklerinden (insanların kaçınılmaz olarak karşılaşacakları) kaçınmak aşırıdır. Kısa, net kod genellikle daha okunabilirdir. Burada deli perl veya bash tek gömleklerden bahsetmiyoruz, sadece gerçekten temel dil özellikleri.
alan ocallaghan

1
Emin değilim, bu cevaptaki aşağı oyların ne için verildiği. Bana göre burada belirtilen her şey doğru ve önemlidir ve her geliştirici için iyi bir tavsiye. Özellikle akıllı-eşek programlama kısmı, while(n)bunun için en kötü örnek olmasa da (daha çok "seviyorum" if(strcmp(one, two)))
Kai Huppmann

1
Ayrıca, üretimde kullanılması gereken C kodu arasındaki farkı bilmeyen i++ve ++ideğiştiren kimseye gerçekten izin vermemelisiniz.
Klutt

2
@PaulMcCarthy İkimiz de kod okunabilirliği için çalışıyoruz. Ancak bunun ne anlama geldiğine katılmıyoruz. Ayrıca, nesnel değildir. Bir kişi için okunması kolay diğeri için zor olabilir. Kişilik ve arka plan bilgisi bunu güçlü bir şekilde etkiler. Asıl mesele, bazı kuralları körü körüne takip ederek maksimum okunabilirlik elde etmemenizdir.
Klutt

0

'%' Orijinalinin ya da modern tanımının daha iyi olup olmadığını tartışmam, ancak böyle bir kısa işleve iki dönüş ifadesi yazan herkes C programlamayı hiç öğretmemelidir. Ekstra dönüş bir goto ifadesidir ve C'de goto kullanmayız. Ayrıca sıfır kontrolü olmayan kod aynı sonuca sahip olur, ekstra geri dönüş okumayı zorlaştırır.


4
"Ekstra getiri bir goto ifadesidir ve C'de goto kullanmayız." - bu çok geniş bir genellemenin ve çok uzak bir eserin birleşimidir.
SS Anne

1
“Biz” kesinlikle C'de kullanıyoruz. Bunda yanlış bir şey yok.
Klutt

1
Ayrıca, gibi bir işlevle ilgili yanlış bir şey yokint findChar(char *str, char c) { if(!str) return -1; int i=0; while(str[i]) { if(str[i] == c) return i; i++; } return -1; }
klutt

1
@PeterKrassoi Okuması zor kodları savunmuyorum, ancak kodun basit bir goto veya ekstra dönüş ifadesinden kaçınmak için kodun tam bir karmaşaya benzediği tonlarca örnek gördüm. İşte goto'nun uygun kullanımına sahip bazı kodlar. Kodları daha kolay okunmasını ve
yönetilmesini

1
@PeterKrassoi Lütfen bu sürümünüzü de gösterin: pastebin.com/qBmR78KA
klutt

0

Sorun ifadesi kafa karıştırıcıdır, ancak sayısal örnek , karenin içindeki rakamın toplamının anlamını açıklar . İşte geliştirilmiş bir sürüm:

C ve C ++ ortak alt kümesine [-10 7 , 10 7 ]n aralığında bir tamsayı alan ve taban 10'daki temsili basamaklarının karelerinin toplamını döndüren bir işlev yazın . Örnek: eğer , işleviniz dönmelidir (1 2 + 2 2 + 3 2 = 14).n12314

Yazdığınız işlev 2 ayrıntı dışında gayet iyi:

  • Bağımsız değişken türde olmalıdır longtipi olarak belirli bir aralıktaki tüm değerleri için yerleştirmek için longbu nedenle bir bütün değerlerini temsil etmek için yeterli bir aralığı, en azından 31 değeri bit olması Cı Standardı tarafından garanti edilmektedir [-10 7 , 10, 7 ] . (Türün int, maksimum değeri olan dönüş türü için yeterli olduğunu unutmayın 568.)
  • %Negatif işlenenlerin davranışı sezgisel değildir ve özellikleri C99 Standardı ve önceki sürümler arasında değişiklik göstermiştir. Negatif girdiler için bile yaklaşımınızın neden geçerli olduğunu belgelemelisiniz.

İşte değiştirilmiş bir sürüm:

int sum_of_digits_squared(long n) {
    int s = 0;

    while (n != 0) {
        /* Since integer division is defined to truncate toward 0 in C99 and C++98 and later,
           the remainder of this division is positive for positive `n`
           and negative for negative `n`, and its absolute value is the last digit
           of the representation of `n` in base 10.
           Squaring this value yields the expected result for both positive and negative `c`.
           dividing `n` by 10 effectively drops the last digit in both cases.
           The loop will not be entered for `n == 0`, producing the correct result `s = 0`.
         */
        int c = n % 10;
        s += c * c;
        n /= 10;
    }
    return s;
}

Öğretmenin cevabının birden fazla kusuru vardır:

  • türünde intdeğerlerin aralığı yetersiz olabilir.
  • özel durum değeri gerekmez 0.
  • negatif değerleri reddetmek gereksizdir ve için tanımlanmamış bir davranışa sahip olabilir n = INT_MIN.

Sorun ifadesindeki (C99 ve değer aralığı n) ekstra kısıtlamalar göz önüne alındığında , sadece ilk hata bir sorundur. Ek kod hala doğru cevapları üretir.

Bu testte iyi bir not almalısınız, ancak yazılı bir testte açıklamayı negatif konularla ilgili anlayışınızı göstermek için gereklidir n, aksi takdirde öğretmen farkında olmadığınızı ve şanslı olduğunuzu varsayabilir. Sözlü bir sınavda, bir soru alırsınız ve cevabınız çivilenirdi.

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.