"Mahalliy o'zgaruvchi ishga tushirilmagan bo'lishi mumkin" dan qanday qochish kerak?

/*This is a program that calculates Internet advertising rates based on what features/options you choose.
 * 
 *  
 */

import java.util.Scanner;

public class InternetAdvertising 
{
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);

        int numberOfWords;      

        //I assigned 0 values to both as Eclipse suggested
        float textCost = 0;
        float linkCost = 0;     

        float graphicCost;

        //<=25 words is a flat fee of $.40 per word plus Base fee of $3.00 
        final float TEXT_FLAT_FEE = 0.40F;
        final float TEXT_BASE_FEE = 3.00F;

        //<=35 words is $.40 for the first 25 words and 
        //an additional $.35 per word up to and including 35 words plus Base fee of $3.00 
        final float LESS_OR_EQUAL_THAN_THIRTYFIVE = 0.35F;

        //Over 35 words is a flat fee of $.32 per word with no base fee
        final float MORE_THAN_THIRTYFIVE = 0.32F;


        System.out.println("Welcome!");

        System.out.print("Enter the number of words in your ad: ");
        numberOfWords = in.nextInt();

        if (numberOfWords <= 25)
        {
            textCost = TEXT_BASE_FEE + (TEXT_FLAT_FEE * numberOfWords);
        }

        else if (numberOfWords <= 35)
        {
            textCost = TEXT_BASE_FEE + (TEXT_FLAT_FEE * 25) + (numberOfWords - 25) * LESS_OR_EQUAL_THAN_THIRTYFIVE;
        }

        else if (numberOfWords > 35)
        {
            textCost = numberOfWords * MORE_THAN_THIRTYFIVE;
        }


        String addLink, advancePay;
        char link, advPay;

        final float LINK_FLAT_FEE = 14.95F;
        final float THREE_MONTH_ADV_DISCOUNT = 0.10F;

        System.out.print("Would you like to add a link (y = yes or n = no)? ");
        addLink = in.next();

        link = addLink.charAt(0);
        link = Character.toLowerCase(link); 

        if (link == 'y')
        {
            System.out.print("Would you like to pay 3 months in advance " + "(y = yes or n = no)? ");
            advancePay = in.next();

            advPay = advancePay.charAt(0);
            advPay = Character.toLowerCase(advPay);

            switch (advPay)
            {
                case 'y':

                    linkCost = (3 * LINK_FLAT_FEE) - (3 * LINK_FLAT_FEE) * THREE_MONTH_ADV_DISCOUNT;

                    break;

                case 'n':

                    linkCost = LINK_FLAT_FEE;

                    break;
            }               
        }

        else
        {
            linkCost = 0;
        }


        String addGraphic;
        char graphic;

        System.out.print("Would you like to add graphics/pictures” + “(S = Small, M = Medium, L = Large or N = None)? ");
        addGraphic = in.next();

        graphic = addGraphic.charAt(0);
        graphic = Character.toUpperCase(graphic);
        graphic = Character.toLowerCase(graphic);       
        switch (graphic)
        {
            case 's':

                graphicCost = 19.07F;

                break;

            case 'm':

                graphicCost = 24.76F;

                break;

            case 'l':

                graphicCost = 29.33F;

                break;

            default:
                graphicCost = 0;
        }


        float gst, totalBeforeGst, totalAfterGst;

        final float GST_RATE = 0.05F;

        totalBeforeGst = textCost + linkCost + graphicCost; //textCost & linkCost would not initialize

        gst = totalBeforeGst * GST_RATE;

        totalAfterGst = totalBeforeGst + (totalBeforeGst * GST_RATE);


        System.out.printf("\t\t%-16s %11s\n", "Category", "Cost");
        System.out.printf("\t\t%-16s %11.2f\n", "Text", textCost);  //linkCost would not initialize
        System.out.printf("\t\t%-16s %11.2f\n", "Link", linkCost);  //textCost would not initialize 
        System.out.printf("\t\t%-16s %11.2f\n", "Graphic", graphicCost);
        System.out.printf("\t\t%-16s %11.2f\n", "Total", totalBeforeGst);
        System.out.printf("\t\t%-16s %11.2f\n", "GST", gst);
        System.out.printf("\t\t%-16s %11.2f\n", "Total with GST", totalAfterGst);
    }   
}

Men bu kod bilan deyarli tugatdim va Eclipse textCost va linkCostga 0 qiymat belgilashimni taklif qilmoqda. Bu muammoni hal qilishning boshqa yo'li bormi? Agar 0 qiymatni tayinlamasam, ular xatoga yo'l qo'yadi (XXX mahalliy o'zgaruvchisi ishga tushirilmagan bo'lishi mumkin). Kimdir menga tenglamalar bilan tayinlangan ikkala o'zgaruvchiga ega bo'lsam ham, nima uchun bu sodir bo'lishini tushuntira oladimi?

rahmat.

EDIT: Men taklif qilinganidek qildim va o'zgaruvchilarni faqat menga kerak bo'lganda e'lon qildim. Men ham ba'zi izohlar qo'shdim.


person HelloWorld    schedule 18.10.2009    source manba
comment
Men javobimni juda ko'p refaktoring bilan yangiladim. Umid qilamanki, sizga yoqadi.   -  person Jon Skeet    schedule 19.10.2009


Javoblar (8)


Kodni chuqurroq o'rganishdan oldin uchta taklif:

  • Kodni tushunishni osonlashtirish uchun o'zgaruvchilarni iloji boricha kechroq e'lon qiling.
  • Ushbu gigant usulni qayta ko'rib chiqing - bu hozirda o'qib bo'lmaydigan darajada katta.
  • Konstantalar static final maydonlarini hosil qiling. Ular usulga qo'ng'iroq qilish bilan bog'liq emas, shuning uchun ular mahalliy o'zgaruvchilar bo'lmasligi kerak.

Endi haqiqiy savolga kelsak, eng oddiy yo'l - har qanday mumkin bo'lgan oqim haqiqatda bajarish yoki istisno qilishiga ishonch hosil qilishdir. Shunday qilib, textCost uchun kodingizni quyidagicha o'zgartiring:

if (numberOfWords <= 25)
{
    textCost = TEXT_BASE_FEE + (TEXT_FLAT_FEE * numberOfWords);
}
else if (numberOfWords <= 35)
{
    textCost = TEXT_BASE_FEE + (TEXT_FLAT_FEE * 25) + (numberOfWords - 25) * 
               LESS_OR_EQUAL_THAN_THIRTYFIVE;
}
else // Note - no condition.
{
    textCost = numberOfWords * MORE_THAN_THIRTYFIVE;
}

linkCost uchun switch iborasini quyidagiga o'zgartiring:

switch (advPay)
{
    case 'y':
        linkCost = (3 * LINK_FLAT_FEE) - 
                   (3 * LINK_FLAT_FEE) * THREE_MONTH_ADV_DISCOUNT;
        break;
    case 'n':
        linkCost = LINK_FLAT_FEE;
        break;
    default:
        throw new Exception("Invalid value specified: " + advPay);
}

Endi siz bu erda istisno qilishni xohlamasligingiz mumkin. Siz yana aylana yoki shunga o'xshash narsalarni xohlashingiz mumkin. Siz shunchaki Exception dan foydalanishni xohlamaysiz - lekin siz foydalanmoqchi bo'lgan aniq istisno turi haqida o'ylab ko'rishingiz kerak.

Buni qilish har doim ham mumkin emas. Aniq topshiriqni aniqlash uchun kompilyator qoidalarinisbatan oddiy. Agar siz kompilyatorni shunday xursand qilish uchun kodni o'zgartira olmagan bo'lsangiz, shunchaki soxta boshlang'ich qiymatni belgilashingiz mumkin. Mumkin bo'lgan joyda bundan qochishga harakat qilishni tavsiya qilaman. Birinchi holda, qiymat haqiqatan ham har doim tayinlangan bo'lar edi - lekin ikkinchi holatda siz haqiqatdan ham qiymatni bermagansiz advPay na 'y' na 'n' bo'lsa, bu qiyin natijaga olib kelishi mumkin. - muammoni keyinroq aniqlash uchun. Kompilyator xatosi bunday muammolarni aniqlashga yordam beradi.

Shunga qaramay, men sizga ushbu usulni qayta ishlashni tavsiya qilaman. O'ylaymanki, har bir usulda atigi 10 qator kod mavjud bo'lganda va har bir o'zgaruvchi birinchi foydalanishdan oldin yoki e'lon qilinganda, nima uchun narsalar aniq belgilanmaganligini tushunish osonroq bo'ladi.

Tahrirlash:

OK, tubdan qayta tiklangan kod quyida keltirilgan. Men bu dunyodagi eng yaxshi kod deb da'vo qilmoqchi emasman, lekin:

  • Bu ko'proq sinovdan o'tkaziladi. Uning har bir qismi uchun birlik testlarini osongina yozishingiz mumkin. printAllCosts unchalik oson sinovdan o'tkazilmaydi, lekin sizda chop etish uchun Writer kerak bo'lgan ortiqcha yuk bo'lishi mumkin - bu yordam beradi.
  • Hisoblashning har bir biti mantiqiy joyda. Havolalar va grafikalar mumkin bo'lgan qiymatlarning kichik to'plamiga ega - Java enumlari bu erda tabiiy ravishda mos keladi. (Bilaman, ular sizning hozirgi mahorat darajangizdan ancha yuqori bo'lishi mumkin, ammo nima mavjud bo'lishini ko'rish yaxshi.)
  • Men endi ikkilik suzuvchi nuqtali raqamlardan foydalanmayapman, chunki ular raqamlar uchun mos emas. Buning o'rniga, men hamma joyda tsentning butun sonini ishlataman va ko'rsatish uchun BigDecimal ga aylantiraman. Qo'shimcha ma'lumot olish uchun .NET floating point dagi maqolamga qarang - bularning barchasi Java bilan haqiqatan ham tegishli. .
  • Reklamaning o'zi endi sinfga kiritilgan. Bu yerga kerak bo'lganda ko'proq ma'lumotlarni qo'shishingiz mumkin.
  • Kod paketda. Tan olish kerakki, hozir hammasi bitta faylda (shuning uchun faqat EntryPoint klassi ochiq), lekin bu faqat Stack Overflow uchun va men Eclipse-ni ochishim shart emas.
  • JavaDoc nima bo'layotganini tushuntiradi - hech bo'lmaganda bir nechta usullar uchun. (Ehtimol, haqiqiy kodga yana bir oz qo'shgan bo'lardim. Bu erda vaqtim tugayapti.)
  • Biz foydalanuvchi kiritgan maʼlumotlarni tasdiqlaymiz, soʻzlar soni bundan mustasno – va biz bu tekshirishni bitta tartibda amalga oshiramiz, bu esa oqilona sinovdan oʻtkazilishi kerak. Keyin biz kiritishni so'raganimizda, bizda haqiqiy narsa bor deb taxmin qilishimiz mumkin.
  • EntryPoint dagi statik usullarning soni biroz xavotirli. Bu unchalik yomon emas, lekin men buni ko'pincha dasturga kirish nuqtasi atrofidagi yo'l deb bilaman. E'tibor bering, u erda to'lovlarga hech qanday aloqasi yo'q - bu asosan faqat foydalanuvchi interfeysi.

Bu yerda avvalgidan ko'ra ko'proq kod bor - lekin u (IMO) ancha o'qilishi va saqlab qo'yilishi mumkin bo'lgan kod.

package advertising;

import java.util.Scanner;
import java.math.BigDecimal;

/** The graphic style of an advert. */
enum Graphic
{
    NONE(0),
    SMALL(1907),
    MEDIUM(2476),
    LARGE(2933);

    private final int cost;

    private Graphic(int cost)
    {
        this.cost = cost;
    }

    /** Returns the cost in cents. */
    public int getCost()
    {
        return cost;
    }
}

/** The link payment plan for an advert. */
enum LinkPlan
{
    NONE(0),
    PREPAID(1495), // 1 month
    POSTPAID(1495 * 3 - (1495 * 3) / 10); // 10% discount for 3 months up-front

    private final int cost;

    private LinkPlan(int cost)
    {
        this.cost = cost;
    }

    /** Returns the cost in cents. */
    public int getCost()
    {
        return cost;
    }
}

class Advertisement
{
    private final int wordCount;
    private final LinkPlan linkPlan;
    private final Graphic graphic;

    public Advertisement(int wordCount, LinkPlan linkPlan, Graphic graphic)
    {
        this.wordCount = wordCount;
        this.linkPlan = linkPlan;
        this.graphic = graphic;
    }

    /**
     * Returns the fee for the words in the advert, in cents.
     * 
     * For up to 25 words, there's a flat fee of 40c per word and a base fee
     * of $3.00.
     * 
     * For 26-35 words inclusive, the fee for the first 25 words is as before,
     * but the per-word fee goes down to 35c for words 26-35.
     * 
     * For more than 35 words, there's a flat fee of 32c per word, and no
     * base fee.     
     */
    public int getWordCost()
    {
        if (wordCount > 35)
        {
            return 32 * wordCount;
        }
        // Apply flat fee always, then up to 25 words at 40 cents,
        // then the rest at 35 cents.
        return 300 + Math.min(wordCount, 25) * 40
                   + Math.min(wordCount - 25, 0) * 35;        
    }

    /**
     * Displays the costs associated with this advert.
     */
    public void printAllCosts()
    {
        System.out.printf("\t\t%-16s %11s\n", "Category", "Cost");
        printCost("Text", getWordCost());
        printCost("Link", linkPlan.getCost());
        printCost("Graphic", graphic.getCost());
        int total = getWordCost() + linkPlan.getCost() + graphic.getCost();
        printCost("Total", total);
        int gst = total / 20;
        printCost("GST", gst);
        printCost("Total with GST", total + gst);
    }

    private void printCost(String category, int cents)
    {
        BigDecimal dollars = new BigDecimal(cents).scaleByPowerOfTen(-2);
        System.out.printf("\t\t%-16s %11.2f\n", category, dollars);
    }
}

/**
 * The entry point for the program - takes user input, builds an 
 * Advertisement, and displays its cost.
 */
public class EntryPoint
{
    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);

        System.out.println("Welcome!");
        int wordCount = readWordCount(scanner);
        LinkPlan linkPlan = readLinkPlan(scanner);
        Graphic graphic = readGraphic(scanner);

        Advertisement advert = new Advertisement(wordCount, linkPlan, graphic);
        advert.printAllCosts();
    }

    private static int readWordCount(Scanner scanner)
    {
        System.out.print("Enter the number of words in your ad: ");
        // Could add validation code in here
        return scanner.nextInt();
    }

    private static LinkPlan readLinkPlan(Scanner scanner)
    {
        System.out.print("Would you like to add a link (y = yes or n = no)? ");
        char addLink = readSingleCharacter(scanner, "yn");
        LinkPlan linkPlan;
        if (addLink == 'n')
        {
            return LinkPlan.NONE;
        }
        System.out.print("Would you like to pay 3 months in advance " +
                         "(y = yes or n = no)? ");
        char advancePay = readSingleCharacter(scanner, "yn");
        return advancePay == 'y' ? LinkPlan.PREPAID : LinkPlan.POSTPAID;
    }

    private static Graphic readGraphic(Scanner scanner)
    {
        System.out.print("Would you like to add graphics/pictures? " +
            "(s = small, m = medium, l = large or n = None)? ");
        char graphic = readSingleCharacter(scanner, "smln");
        switch (graphic)
        {
            case 's': return Graphic.SMALL;
            case 'm': return Graphic.MEDIUM;
            case 'l': return Graphic.LARGE;
            case 'n': return Graphic.NONE;
            default:
                throw new IllegalStateException("Unexpected state; graphic=" +
                                                graphic);
        }
    }

    private static char readSingleCharacter(Scanner scanner,
                                            String validOptions)
    {
        while(true)
        {
            String input = scanner.next();
            if (input.length() != 1 || !validOptions.contains(input))
            {
                System.out.print("Invalid value. Please try again: ");
                continue;
            }
            return input.charAt(0);
        }
    }
}
person Jon Skeet    schedule 18.10.2009
comment
Menimcha, bu ikkala taklif ham yaxshi g'oyalar. O'zgaruvchi e'lon qilinmaganligi haqidagi ogohlantirish ur (boshlanmagan, o'qilgan) nosozliklarni oldini olish uchun muhimdir. Usulning siklomatik raqami, mening hisobim bo'yicha, 7 ga teng, shuning uchun funktsiya ham unnacacary katta emas. - person Red33mer; 18.10.2009
comment
Tsiklomatik murakkablik o'qilishi mumkin bo'lgan hamma narsa emas. Usul 125 satrdan iborat bo'lib, ishonchim komilki, unijudaosonlik bilan ajratish mumkin. O'zgaruvchilarni iloji boricha kechroq e'lon qilish ishga tushirilmagan o'zgaruvchilar haqidagi ogohlantirishni olib tashlamaydi - bu ogohlantirishni tushunishni osonlashtiradi. Men ikkala taklifimga ham sodiqman. - person Jon Skeet; 18.10.2009
comment
... va endi men uchinchisini qo'shyapman. - person Jon Skeet; 18.10.2009
comment
Buni ajratishning yana bir sababi sinovdan o'tishdir. Hozirgi usul asosan sinovdan o'tkazilmaydi. (Ha, siz buni mumkin qila olasiz, lekin bu aqldan ozar edi.) Har bir qiymatni kirish ma'lumotlaridan alohida ishlab chiqish uchun usulni mantiqiy bo'laklarga bo'lish uni ko'p sinovdan o'tkazish osonroq. - person Jon Skeet; 18.10.2009
comment
Rahmat, Jon. O‘qishni boshlaganimga endigina bir oy bo‘ldi. Shunday qilib, taklifning ba'zilari hali ham men uchun biroz begonadek tuyuladi, lekin men yangi narsalarni o'rganayotganim yaxshi. Men hali ham bir nechta usullarni qanday yaratishni va ularni qanday qilib to'g'ri ajratish kerakligini bilmayman. - person HelloWorld; 18.10.2009
comment
@HelloWorld - bu haqda tashvishlanmang, siz u erga borasiz. Ammo men jiddiy ravishda sizga ko'proq borishdan oldin boshqa usullar va turlarni yaratish haqida ko'proq ma'lumot olishni boshlashni tavsiya qilaman. Xususan, ko'proq asoslarni bilmaguningizcha, foydali narsalar uchun haqiqiy dasturlashni boshlashga urinmang. - person Jon Skeet; 18.10.2009
comment
Agar namuna kodingizni olishga va qanday ko'rinishini ko'rsatish uchun uni qayta tahrirlashga harakat qilsam, bu yordam beradimi? - person Jon Skeet; 18.10.2009
comment
+1 Yadro java uchun yangi bo'lganim uchun aniq va aniq javoblarni ko'rganimdan juda minnatdorman. Belgilangan :-) - person Edward J Beckett; 26.10.2012

link == 'y' va advPay 'y' yoki 'n' bo'lmasa, linkCost ishga tushirilmaydi.

Boshqacha qilib aytganda, kompilyator sizning kodingiz orqali mahalliy o'zgaruvchini ishlatishdan oldin ishga tushirilmagan yo'lni topishi mumkin bo'lganda, siz ushbu xatoni olasiz.

person eljenso    schedule 18.10.2009

Buning sababi, topshiriq shartli ichida sodir bo'ladi, agar shart bajarilmasa, topshiriq hech qachon sodir bo'lmaydi.

xatolikka yo'l qo'ymaslik uchun shartli qiymatdan tashqari qiymat belgilashingiz kerak (eng keng tarqalgani 0 bo'ladi).

person josemrb    schedule 18.10.2009

Eclipse tahlili o'zgaruvchining har bir kod yo'liga tayinlanganligini aniqlash uchun numberOfWords dagi testlar hech qachon muvaffaqiyatsiz bo'lmasligini tushunish uchun yetarli darajada aqlli emasligini aniqlash uchun amalga oshiradi. Odatda, barcha mumkin bo'lgan shartlarni statik baholash mumkin emasligi sababli, kompilyator/sintaksis tekshiruvi ularning hech birini baholashga urinmaydi. Agar siz oxirgi "else if" ni "else" bilan almashtirgan bo'lsangiz, u ishlashi kerak, chunki sinovdan o'tayotgan shartlardan qat'i nazar, textCost ga kamida bitta topshiriq amalga oshiriladi.

person Nicholas Riley    schedule 18.10.2009
comment
Men textCost uchun taklif qilganingizdek qildim va else bo'lsa, else bilan almashtirdim. rahmat. - person HelloWorld; 18.10.2009

Eclipse sizni ogohlantirmoqda, chunki sizning ishga tushirishlaringiz shartlar ichida sodir bo'ladi. Agar shartlardan hech biri bajarilmasa, textCost ishga tushirilmaydi.

if (numberOfWords <= 25)
    {
            //assign a value to textCost
    }
    else if (numberOfWords <= 35)
    {
            //assign a value to textCost
    }
    else if (numberOfWords > 35)
    {
            //assign a value to textCost
    }

Eclipse, ehtimol, (numberOfWords <= 35) va (numberOfWords > 35) barcha imkoniyatlarni qamrab olishini tan olmaydi.

Siz uni deklaratsiyada 0 ga boshlashingiz yoki uni nolga o'rnatadigan qo'shimcha boshqa {} qo'shishingiz mumkin.

Boshqa o'zgaruvchiga o'xshash tushuntirish.

person hexium    schedule 18.10.2009
comment
Siz aytmoqchi bo'lgansiz (numberOfWords ‹= 35) va (numberOfWords › 35). La'nat nusxa ko'chirish/joylashtirish;) - person ThisSuitIsBlackNot; 18.10.2009
comment
Rahmat, men ham buni payqadim va siz javob berganingizda uni tuzatdim. :) - person hexium; 18.10.2009
comment
Bundan tashqari, siz else if (numberOfWords > 35) ni faqat else bilan almashtirishingiz mumkin. Va Eclipse, ehtimol, barcha imkoniyatlar qamrab olinganligini tan olishi mumkin emas; Java aslida ishga tushirilmagan bo'lishi mumkin bo'lgan vaqtni chiqarish bo'yicha belgilangan qoidalarga ega va bu holat ularga ta'sir qilishi mumkin. - person JaakkoK; 18.10.2009

xato xabari sizga bu o'zgaruvchilar har doim ishga tushirilmasligini bildiradi. Buning sababi shundaki, sizning ishga tushirishingiz faqat ma'lum sharoitlarda sodir bo'ladi (ular if-iboralarida joylashgan). Umid qilamanki, bu yordam beradi ..

person andyp    schedule 18.10.2009

NumberOfWords bilan taqqoslashning 3 ta tarmog'idan biriga tashrif buyurishini bilsangiz ham, kompilyator buni bilmaydi. Bu else bandini kiritish mumkin deb noto'g'ri taxmin qiladi va textCost o'zgaruvchisi birlashtirilgan holda qoladi.

Xuddi shunday switch (advPay) bilan. Garchi siz ikkisidan biriga tashrif buyurishinibilsangiz ham, kompilyator buni bilmaydi.

Taklif: else if (numberOfWords > 35) ni olib tashlang, uni faqat else qilib qo'ying.

switch (advPay) ga kelsak, default holatini qo'shing. Ichkarida siz throw new AssertionError(); qo'yishingiz mumkin.

person RichN    schedule 18.10.2009
comment
Shunchaki switch iborasini olib tashlab, o'rnatilgan if iborasini yaratsam yaxshi bo'larmidi? - person HelloWorld; 18.10.2009
comment
Aslida hech qanday farq qilmaydi. Shunga qaramay, sizga boshqa band kerak. - person RichN; 18.10.2009

Bunday muammolarni oldini olishning yaxshi usuli - tayinlangan o'zgaruvchini tekshirishdan oldin final va initiallashtirilmagan sifatida o'rnatishdir. Bu uni ishlatish/o'qishdan oldin qiymatni belgilashga majbur qiladi.

final textCostTmp;
if (condition1) {
  textCostTmp = ...;
} else if (condition2) {
  textCostTmp = ...;
} 
// if you use textCostTmp here the compiler will complain that it is uninitialized !
textCost = textCostTmp;

Buni hal qilish uchun o'zgaruvchini ishga tushirmang, chunki etishmayotgan else holatini o'tkazib yuborishingiz mumkin. Yagona to'g'ri yechim barcha mumkin bo'lgan holatlarni qamrab olish uchun else holatni qo'shishdir! Men yakuniy bo'lmagan o'zgaruvchilarni ishga tushirishni yomon amaliyot deb hisoblayman, masalan, tsikldagi hisoblagich kabi ba'zi noyob stsenariylar bundan mustasno.

Taklif etilayotgan yondashuv sizni hozir va kelajakda barcha mumkin bo'lgan holatlarni ko'rib chiqishga majbur qiladi (saqlash osonroq). Ba'zida kompilyator biroz ahmoq bo'ladi (o'sha raqamOfWords > 35 ekanligini tushunolmayman) ... lekin kompilyator sizning dushmaningiz emas ...

person Christophe Roussy    schedule 11.08.2014