天花板編程手把手計(jì)劃-第1期-第1天

1. 編程是一項(xiàng)綜合技能

1.1 編程和語言

之前的系列中,我們重點(diǎn)關(guān)注的是如何學(xué)會一門計(jì)算機(jī)語言。而在這個系列中,我們會重點(diǎn)關(guān)注如何編程。編程是一項(xiàng)綜合技能,它主要包括了以下幾個方面:

  • 計(jì)算機(jī)語言的掌握
  • 編程工具的使用
  • 程序設(shè)計(jì)思想
  • 調(diào)試技巧
  • 代碼分析能力

很顯然,僅僅是對語言的掌握還遠(yuǎn)遠(yuǎn)不夠。很多人學(xué)習(xí)編程都扎進(jìn)了語言學(xué)習(xí)的泥潭中難以自拔。計(jì)算機(jī)語言無疑是編程中最枯燥的一部分,如果不把它與其他的技能聯(lián)系起來學(xué)習(xí)很容易讓人失去興趣。

我們這個天花板編程手把手計(jì)劃就是要帶著大家從這幾個方面入手,以做練習(xí)的方式多角度地學(xué)習(xí)編程。也許作為初學(xué)者的你只掌握了最基礎(chǔ)的計(jì)算機(jī)語言,但你會發(fā)現(xiàn)用最簡單的語法也能寫出相對復(fù)雜的功能。通過成就感引領(lǐng)你繼續(xù)探索才是學(xué)習(xí)編程最好的方法。

1.2 IDE之爭

在微信群里,總有人問我能不能不用VS,能不能用某某IDE。在這個系列中,我強(qiáng)行規(guī)定了大家必須使用VS,而且是VS2010以上的版本。原因主要有以下兩點(diǎn):

第一,我們的練習(xí)過程中,會涉及到一些代碼調(diào)試的技巧,不同IDE的具體操作方法是不同的,如果不統(tǒng)一就很難通過文字的方式進(jìn)行手把手的教授。

第二,在Windows平臺上,VS是目前業(yè)內(nèi)使用最廣泛的IDE,從某種意義上講,VS的使用是一個必備的技能點(diǎn)。如果你不會,那么找工作時可能會減分。

1.3 手把手方式

由于沒辦法拿出整塊的時間做輔導(dǎo),因此我的線下輔導(dǎo)主要是以微信群的方式進(jìn)行。所有報(bào)名的同學(xué)目前都已經(jīng)在群里,在完成作業(yè)的過程中有任何問題可以隨時在微信群中溝通,我會盡可能在第一時間給予回答。

對于一些調(diào)試技巧和工具的使用問題,我也會在群里做一對一的輔導(dǎo)。

2. 報(bào)名作業(yè)

今天先說一下報(bào)名作業(yè)。麻雀雖小,五臟俱全,這個作業(yè)看似容易,做好可不容易。題目是編程實(shí)現(xiàn)如下功能:依次打印出1~100,遇到素?cái)?shù)折行。效果如下:

我們先簡單分析一下。

2.1 功能拆分

遇到一個題目,我們首先要了解它要實(shí)現(xiàn)什么功能,之后再把這個目標(biāo)功能分解成若干個子功能。如果這些子功能都是我們實(shí)現(xiàn)過的,那這個問題就解決了。

這道題可以分為兩個子功能:

  • 打印從1到100這100個數(shù)字
  • 判斷每個數(shù)字是否是素?cái)?shù)

2.2 尋找解決方案

有了這兩個子功能之后,我們需要找到對應(yīng)的解決方案。

打印1~100很容易,每個同學(xué)都能想到下面這段代碼:

int i; 
for (i = 1; i <= 100; i++)
{ 
   printf("%d ", i); 
} 

接下來,判斷素?cái)?shù)成為了這道題的一個小小的難點(diǎn)。

3. 判斷素?cái)?shù)的方法

3.1 什么是素?cái)?shù)

素?cái)?shù)又稱質(zhì)數(shù),是指除了1和它本身以外,不能被任何整數(shù)整除的數(shù)。比如5就是素?cái)?shù),因?yàn)樗荒鼙?~4之間的任何一個整數(shù)整除。

3.2 思路一 : 通過定義實(shí)現(xiàn)

判斷一個整數(shù)n是否是素?cái)?shù),只需把n被 2 ~ n-1 之間的每一個整數(shù)去除,如果都不能被整除,那么n就是一個素?cái)?shù)。 代碼如下:

int i; 
for (i = 2; i < n; i++) 
{ 
    if (n % i == 0) 
    {
        // n不是素?cái)?shù)
        break;
    } 
} 

if (i >= n) // 完成循環(huán)
{
    // n是素?cái)?shù) 
}
else // 循環(huán)被中斷
{
    // n不是素?cái)?shù) 
}

這個方法是我目前最推薦的,因?yàn)閷τ诔鯇W(xué)者通過把自然語言描述的算法直接轉(zhuǎn)換成計(jì)算機(jī)語言去解決問題的能力非常重要。掌握了這個能力,完成功能性代碼就再也難不倒你了。

3.3 思路二 : 算法優(yōu)化

這是一個進(jìn)階的解決方案。在我們解決問題時,需要在完成了基本功能之后思考一下是否有優(yōu)化的可能。程序優(yōu)化主要有兩個點(diǎn):

  • 代碼優(yōu)化

按照“高內(nèi)聚,低耦合”的思想,修改自己的代碼,使它擁有更高的可復(fù)用性和可讀性。這方面的優(yōu)化是為了降低今后的維護(hù)成本。

  • 算法優(yōu)化

通過算法的優(yōu)化提高程序的執(zhí)行效率和空間開銷。

對于這個具體的問題,我們可以通過數(shù)學(xué)方法優(yōu)化算法。

n 并不用被 2 ~ n-1 之間的每一個整數(shù)去除,只需被 2 ~ 根號n 之間的每一個整數(shù)去除就可以了。如果n不能被 2 ~ 根號n 間任一整數(shù)整除,n必定是素?cái)?shù)。

例如 : 判別17是是否為素?cái)?shù),只需使17被2~4之間的每一個整數(shù)去除,由于都不能整除,所以17是素?cái)?shù)。

原因 : 因?yàn)槿绻鹡能被 2 ~ n-1 之間任一整數(shù)整除,它的兩個因子必定有一個小于或等于根號n,另一個大于或等于根號n。

代碼如下:

// 求平方根,注意sqrt()的參數(shù)為 double 類型,這里要強(qiáng)制轉(zhuǎn)換m的類型 
int i;
k = (int)sqrt((double)n);
for (i = 2; i <= k; i++)
{
    if (m % i == 0) 
    {
        break;
    } 
}

if (i >= n) // 完成循環(huán)
{
    // n是素?cái)?shù) 
}
else // 循環(huán)被中斷
{
    // n不是素?cái)?shù) 
}

到這里,有人會說我怎么能夠想到這么一個優(yōu)化算法呢。你可以用萬能的互聯(lián)網(wǎng)啊。

4. 尋找資料也是重要技能

在入門階段,我們會用一些非常基礎(chǔ)的題目做練習(xí)。比如簡單排序,各種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)什么的。但當(dāng)你真正進(jìn)入軟件開發(fā)行業(yè)時,你首先要學(xué)會的思維方式就是不要重復(fù)發(fā)明輪子。每當(dāng)你遇到一個具體的問題,你首先要考慮的是有沒有現(xiàn)有的東西可以用,比如各種開源項(xiàng)目代碼、各種經(jīng)典的庫Boost、STL、MFC等等。對于這些現(xiàn)有的“輪子”,使用它們是你最好的選擇。

因此,從現(xiàn)在起,你做的每一個小項(xiàng)目都要在自己思考解決方案的同時試著在網(wǎng)上找找是否有可以利用的現(xiàn)成資源。對于這些知識的積累才是更有價值的項(xiàng)目經(jīng)驗(yàn)。

另外,在編程時遇到的很多工具報(bào)錯的問題都可以在搜索引擎中找到解決方法。要學(xué)會利用這些資源。舉個簡單的例子,很多人在使用高版本的VS時都會遇到這樣的報(bào)錯信息。

只要你把這串報(bào)錯信息粘貼到搜索引擎中就能找到解決方法,在代碼最前面添加這句話即可:

#define _CRT_SECURE_NO_WARNINGS

如果有好奇心查看相關(guān)資料,你會學(xué)到更多關(guān)于VS代碼安全機(jī)制方面的知識。千萬不要給自己切斷了這么重要的一個學(xué)習(xí)途徑。

5. 功能整合

無論是自己實(shí)現(xiàn),還是利用網(wǎng)絡(luò)資源,我們現(xiàn)在都已經(jīng)找到了全部子功能的解決方案。這時候你的心里應(yīng)該有底了,最后只剩下把這些子功能整合成我們最終需要的完整程序了。

在這個階段,我們最需要考慮的就是代碼的復(fù)用性。說簡單點(diǎn)就是讓自己后續(xù)修改任何一塊子功能的時候都盡可能小的改動代碼。一般我們有兩種方法。

  • 方法一:代碼邏輯分隔法
  • 方法二:函數(shù)劃分法

5.1 代碼邏輯劃分

  • 第一步,我們按照子功能構(gòu)建代碼框架
int main()
{
    int num;
    // 循環(huán)得到100個數(shù)
    for (num = 1; num <= 100; num++)
    {
        /**** 判斷素?cái)?shù) ****/

        /**** 判斷結(jié)束 ****/
        
        /****  打 印 ****/

        /**** 打印結(jié)束 ****/
    }
}

這是一個最基本的代碼框架,把每個子功能的代碼位置留好。

  • 第二步:填空

像填空一樣把每部分的代碼填在相應(yīng)的位置上。這樣可以最大程度的保持每部分代碼的獨(dú)立性。

int main()
{
    int num, i;
    // 循環(huán)得到100個數(shù)
    for (num = 1; num <= 100; num++)
    {
        /**** 判斷素?cái)?shù) ****/
        for (i = 2; i < num; i++)
        {
            if (num % i == 0)
            {
                break;
            }
        }
        /**** 判斷結(jié)束 ****/
        
        /****  打 印 ****/
        if (i >= num && num != 1)
        {
            printf("%d\n", num);
        }
        else
        {
            printf("%d ", num);
        }
        /**** 打印結(jié)束 ****/
    }
    printf("\n");
}
  • 第三歩:代碼整理

細(xì)心的同學(xué)會發(fā)現(xiàn),打印部分的if語句其實(shí)是在判斷之前程序計(jì)算的結(jié)果,因此應(yīng)該放在判斷部分。但如果把這行劃分在前面會破壞if語句的完整性,因此我們利用標(biāo)記變量來讓它們的耦合度更低。

int main()
{
    int num, i;
    int isPrimeNum;
    // 循環(huán)得到100個數(shù)
    for (num = 1; num <= 100; num++)
    {
        /**** 判斷素?cái)?shù) ****/
        if (num == 1)
        {
            isPrimeNum = 0;
        }
        else
        {
            isPrimeNum = 1;
        }

        for (i = 2; i < num; i++)
        {
            if (num % i == 0)
            {
                isPrimeNum = 0;
                break;
            }
        }
        /**** 判斷結(jié)束 ****/

        /****  打 印 ****/
        if (isPrimeNum == 1)
        {
            printf("%d\n", num);
        }
        else
        {
            printf("%d ", num);
        }
        /**** 打印結(jié)束 ****/
    }
    printf("\n");
}

這段代碼中,我們引入了一個標(biāo)記變量isPrimeNum,它作為第一部分向第二部分傳遞判斷結(jié)果的一個變量。正因?yàn)橛辛怂抛屵@兩個部分在邏輯上完全分開了。雖然在判斷1這個特殊情況時,我們多寫了幾行代碼,但從邏輯上更加清晰了,代碼的可讀性也就更強(qiáng)了。

這樣做的好處是什么呢?如果我們現(xiàn)在需要修改判斷素?cái)?shù)的算法,或者需要修改打印方式,都只需要修改一部分的代碼即可。代碼維護(hù)中有個永恒的真理就是:修改的代碼越少,出錯的機(jī)會就越少。

5.2 函數(shù)劃分法

這個方法是在上面方法的基礎(chǔ)上把重要的部分提取成一個獨(dú)立的函數(shù),從邏輯上說耦合性更小。

int IsPrimeNum1(int num)
{
    int i;

    if (num == 1)
    {
        return 0;
    }

    for (i = 2; i < num; i++)
    {
        if (num % i == 0)
        {
            return 0;
        }
    }

    return 1;
}

int IsPrimeNum2(int num)
{
    int i, k;

    if (num == 1)
    {
        return 0;
    }

    k = (int)sqrt((double)num);

    for (i = 2; i <= k; i++)
    {
        if (num % i == 0)
        {
            return 0;
        }
    }

    return 1;
}

int main()
{
    int num, i;
    int isPrimeNum;
    for (num = 1; num <= 100; num++)
    {
        if (IsPrimeNum1(num) == 1)
        //if (IsPrimeNum2(num) == 1)
        {
            printf("%d\n", num);
        }
        else
        {
            printf("%d ", num);
        }
    }
    printf("\n");
}

兩種判斷素?cái)?shù)的方法被寫成兩個獨(dú)立的函數(shù)IsPrimeNum1()和IsPrimeNum2(),如果需要相互替換,可以隨時在main函數(shù)中修改一下調(diào)用代碼。修改一行代碼就能替換一個完整的功能。是不是很神奇。

在實(shí)際的項(xiàng)目中,這兩種方法要結(jié)合使用。究竟在什么時候選擇什么樣的方式,這就是屬于你自己的編程風(fēng)格和項(xiàng)目經(jīng)驗(yàn)。

6. 完成情況

漫天星星55 | a627892820 這兩位同學(xué)都用到了函數(shù)劃分的方法,值得表揚(yáng)。需要注意的是:

  • 上傳代碼要使用代碼框,否則不方便別人閱讀
  • 上傳源碼圖片的方式也不可取
  • 做練習(xí)時最好把源碼文件認(rèn)真歸檔,方便以后復(fù)習(xí)。不要在VS的默認(rèn)目錄中。(別問我怎么發(fā)現(xiàn)的)
  • 代碼風(fēng)格要統(tǒng)一,該有的空格不要省略
  • 變量命名要規(guī)范,a b c這樣的變量名不要用

7. 課后作業(yè)

給出任意一個N*N的矩陣,將里面的數(shù)字按照從左上到右下有小到大排序,之后計(jì)算出新矩陣對角線上的數(shù)字總和(每個位置只參與一次計(jì)算)。例如:

給出左邊這個矩陣,先把它轉(zhuǎn)換成右邊的矩陣,之后計(jì)算對角線上的數(shù)字之和:1 + 5 + 9 + 3 + 7 = 25

請?jiān)?017年4月21日23:00之前完成。完成方式請參考:天花板編程手把手計(jì)劃

我是天花板,讓我們一起在軟件開發(fā)中自我迭代。
如有任何問題,歡迎與我聯(lián)系。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內(nèi)容