[D 語言] 第一項修練--無限迴圈。

OK,這是在這篇『學習新程式語言的十五項修練』中找到的,我找不到原文最原始的網頁,所以附上 digg 的連結,裡頭也有一些討論。

接著會試著用 D 語言來完成這幾個練習題,用的編譯器版本是 GDC v0.24,而標準函式庫則是採用 Tango v0.99.7,作業系統為 Linux 或 Windows,如果有特別的作業系統或第三方函式庫需求,我會特別標出來。

題目採用原來英文版本的,因為題目的英文都很簡單,所以直接用英文版的,應該會比較接近題目的原意。

第一個題目是無限迴圈:

Display series of numbers (1,2,3,4, 5....etc) in an infinite loop. The program should quit if someone hits a specific key (Say ESCAPE key).

看起來雖然像是非常簡單的題目,但事實上並非如此,因為很不幸的,C 語言與 D 語言還有 Tango 的標準輸入輸出函式庫,全都是 line-buffered,到目前為止,我還找不到可以直接用標準函式庫硬幹出來,而且在 Linux 與 Windows 上都可以跑的方法。

我已經在 Stack Overflow 這個網站上提問,不過目前看來情況不怎麼樂觀。

另外,這個題目看起來簡單,但我覺得其實暗藏玄機啊!你要怎麼樣在印迴圈的同時監控使用者有沒有按下 ESC 鍵呢?而且如果使用者沒按 ESC 鍵,就應該要一直在螢幕上顯示數字,不能有停下來等使用者輸入的情況。

到最後,我的解決方式如下--用一個 Thread 來當 Keyboard Handler,專門監測使用者的按鍵動作。當然,因為標準函式庫都是 line-buffered,我只好借助 ncurses 這個 Linux 下常見的函式庫,利用裡面的函式來截取使用者按鍵的動作。

當然,ncurses 是 C 的函式庫,不過既然 D 可以直接與 C 語言進行接合(interface with C),所以這不是問題,只是這麼一來這隻程式就變成只能在 Linux 上跑囉。

以下就是這次的第一項修練,其實程式滿短的,應該很容易理解是在做什麼。而在這個範例中,你可以學到:

  1. 如何使用 C 語言的第三方函式庫。
  2. 如何建立 Thread。
=============================================

import tango.io.Stdout;
import tango.core.Thread;


/******************************************
 * 宣告用到的 ncurses  函式原型,只要像這
 * 樣宣告後,就可以直接在程式碼中用 C  函
 * 式庫中的函式。
 *
 * 編譯時,如果用的是 dsss,就用 
 * dsss -llncurses 連結 ncurses  函式庫。
 *****************************************/

extern(C)
{
    void * initscr();
    int cbreak ();
    int getch();
    int endwin();
    int noecho();
}


/******************************************
 * 這個函式會用來建立一個 Thread 監視使用
 * 者的鍵盤輸入。
 *****************************************/
void keyboardHandler ()
{
    initscr(); // 初始化 ncurses
    cbreak();  // 讓標準輸不要做 buffer
    noecho();  // 不顯示使用者按下的鍵

    // 一直重復等使用者的按鍵輸入,直到使
    // 用者按下 ESC。
    while (getch() != 27) {
    }

    endwin();  // 結束 ncurses
}

/******************************************
 * 主程式,不用講了。
 *****************************************/
void main ()
{
    // 建立並執行監視鍵盤的 Thread
    Thread handler = new Thread (
        &keyboardHandler
    );

    handler.start();

    // 無限迴圈
    for (int i = 0; ; i++) {
        Stdout.format ("{}\r\n", i).flush;

        // 如果建立的 Thread 已經不再執行狀
        // 態,代表使用者按下了 ESC  鍵,跳
        // 出無限迴圈。
        if (handler.isRunning == false) {
            break;
        }
    }

    return 0;
}

回響