Linux中國

通過編寫「猜數字」遊戲來學習 Awk

當你學習一門新的編程語言時,最好把重點放在大多數編程語言都有的共同點上:

  • 變數 —— 存儲信息的地方
  • 表達式 —— 計算的方法
  • 語句 —— 在程序中表示狀態變化的方法

這些概念是大多是編程語言的基礎。

一旦你理解了這些概念,你就可以開始把其他的弄清楚。例如,大多數語言都有由其設計所支持的「處理方式」,這些方式在不同語言之間可能有很大的不同。這些方法包括模塊化(將相關功能分組在一起)、聲明式與命令式、面向對象、低級與高級語法特性等等。許多程序員比較熟悉的是編程「儀式」,即,在處理問題之前設置場景所需花費的工作。據說 Java 編程語言有一個源於其設計的重要儀式要求,就是所有代碼都在一個類中定義。

但從根本上講,編程語言通常有相似之處。一旦你掌握了一種編程語言,就可以從學習另一種語言的基本知識開始,品味這種新語言的不同之處。

一個好方法是創建一組基本的測試程序。有了這些,就可以從這些相似之處開始學習。

你可以選擇創建的一個測試程序是「猜數字」程序。電腦從 1 到 100 之間選擇一個數字,讓你猜這個數字。程序一直循環,直到你猜對為止。

猜數字」程序練習了編程語言中的幾個概念:

  • 變數
  • 輸入
  • 輸出
  • 條件判斷
  • 循環

這是學習一門新的編程語言的一個很好的實踐實驗。

:本文改編自 Moshe Zadka 在 Julia 中使用這種方法和 Jim Hall在 Bash 中使用這種方法的文章。

awk 程序中猜數

讓我們編寫一個實現「猜數字」遊戲的 Awk 程序。

Awk 是動態類型的,這是一種面向數據轉換的腳本語言,並且對交互使用有著令人驚訝的良好支持。Awk 出現於 20 世紀 70 年代,最初是 Unix 操作系統的一部分。如果你不了解 Awk,但是喜歡電子表格,這就是一個你可以 去學習 Awk 的信號!

您可以通過編寫一個「猜數字」遊戲版本來開始對 Awk 的探索。

以下是我的實現(帶有行號,以便我們可以查看一些特定功能):

     1    BEGIN {
     2        srand(42)
     3        randomNumber = int(rand() * 100) + 1
     4        print "random number is",randomNumber
     5        printf "guess a number between 1 and 100n"
     6    }
     7    {
     8        guess = int($0)
     9        if (guess < randomNumber) {
    10            printf "too low, try again:"
    11        } else if (guess > randomNumber) {
    12            printf "too high, try again:"
    13        } else {
    14            printf "that&apos;s rightn"
    15            exit
    16        }
    17    }

我們可以立即看到 Awk 控制結構與 C 或 Java 的相似之處,但與 Python 不同。 在像 if-then-elsewhile 這樣的語句中,thenelsewhile 部分接受一個語句或一組被 {} 包圍的語句。然而,Awk 有一個很大的區別需要從一開始就了解:

根據設計,Awk 是圍繞數據管道構建的。

這是什麼意思呢?大多數 Awk 程序都是一些代碼片段,它們接收一行輸入,對數據做一些處理,然後將其寫入輸出。認識到這種轉換管道的需要,Awk 默認情況下提供了所有的轉換管道。讓我們通過關於上面程序的一個基本問題來探索:「從控制台讀取數據」的結構在哪裡?

答案是——「內置的」。特別的,第 7-17 行告訴 Awk 如何處理被讀取的每一行。在這種情況下,很容易看到第 1-6 行是在讀取任何內容之前被執行的。

更具體地說,第 1 行上的 BEGIN 關鍵字是一種「模式」,在本例中,它指示 Awk 在讀取任何數據之前,應該先執行 { ... }BEGIN 後面的內容。另一個類似的關鍵字 END,在這個程序中沒有被使用,它指示 Awk 在讀取完所有內容後要做什麼。

回到第 7-17 行,我們看到它們創建了一個類似代碼塊 { ... } 的片段,但前面沒有關鍵字。因為在 { 之前沒有任何東西可以讓 Awk 匹配,所以它將把這一行用於接收每一行輸入。每一行的輸入都將由用戶輸入作為猜測。

讓我們看看正在執行的代碼。首先,是在讀取任何輸入之前發生的序言部分。

在第 2 行,我們用數字 42 初始化隨機數生成器(如果不提供參數,則使用系統時鐘)。為什麼要用 42?當然要選 42! 第 3 行計算 1 到 100 之間的隨機數,第 4 行輸出該隨機數以供調試使用。第 5 行邀請用戶猜一個數字。注意這一行使用的是 printf,而不是 print。和 C 語言一樣,printf 的第一個參數是一個用于格式化輸出的模板。

既然用戶知道程序需要輸入,她就可以在控制台上鍵入猜測。如前所述,Awk 將這種猜測提供給第 7-17 行的代碼。第 18 行將輸入記錄轉換為整數;$0 表示整個輸入記錄,而 $1 表示輸入記錄的第一個欄位,$2 表示第二個欄位,以此類推。是的,Awk 使用預定義的分隔符(默認為空格)將輸入行分割為組成欄位。第 9-15 行將猜測結果與隨機數進行比較,列印適當的響應。如果猜對了,第 15 行就會從輸入行處理管道中提前退出。

就這麼簡單!

考慮到 Awk 程序不同尋常的結構,代碼片段會對特定的輸入行配置做出反應,並處理數據,讓我們看看另一種結構,看看過濾部分是如何工作的:

     1    BEGIN {
     2        srand(42)
     3        randomNumber = int(rand() * 100) + 1
     4        print "random number is",randomNumber
     5        printf "guess a number between 1 and 100n"
     6    }
     7    int($0) < randomNumber {
     8        printf "too low, try again: "
     9    }
    10    int($0) > randomNumber {
    11        printf "too high, try again: "
    12    }
    13    int($0) == randomNumber {
    14        printf "that&apos;s rightn"
    15        exit
    16    }

第 1–6 行代碼沒有改變。但是現在我們看到第 7-9 行是當輸入整數值小於隨機數時執行的代碼,第 10-12 行是當輸入整數值大於隨機數時執行的代碼,第 13-16 行是兩者相等時執行的代碼。

這看起來「很酷但很奇怪」 —— 例如,為什麼我們會重複計算 int($0)?可以肯定的是,用這種方法來解決問題會很奇怪。但這些模式確實是分離條件處理的非常好的方式,因為它們可以使用正則表達式或 Awk 支持的任何其他結構。

為了完整起見,我們可以使用這些模式將普通的計算與只適用於特定環境的計算分離開來。下面是第三個版本:

     1    BEGIN {
     2        srand(42)
     3        randomNumber = int(rand() * 100) + 1
     4        print "random number is",randomNumber
     5        printf "guess a number between 1 and 100n"
     6    }
     7    {
     8        guess = int($0)
     9    }
    10    guess < randomNumber {
    11        printf "too low, try again: "
    12    }
    13    guess > randomNumber {
    14        printf "too high, try again: "
    15    }
    16    guess == randomNumber {
    17        printf "that&apos;s rightn"
    18        exit
    19    }

認識到這一點,無論輸入的是什麼值,都需要將其轉換為整數,因此我們創建了第 7-9 行來完成這一任務。現在第 10-12、13-15 和 16-19 行這三組代碼,都是指已經定義好的變數 guess,而不是每次都對輸入行進行轉換。

讓我們回到我們想要學習的東西列表:

  • 變數 —— 是的,Awk 有這些;我們可以推斷出,輸入數據以字元串形式輸入,但在需要時可以轉換為數值
  • 輸入 —— Awk 只是通過它的「數據轉換管道」的方式發送輸入來讀取數據
  • 輸出 —— 我們已經使用了 Awk 的 printprintf 函數來將內容寫入輸出
  • 條件判斷 —— 我們已經學習了 Awk 的 if-then-else 和對應特定輸入行配置的輸入過濾器
  • 循環 —— 嗯,想像一下!我們在這裡不需要循環,這還是多虧了 Awk 採用的「數據轉換管道」方法;循環「就這麼發生了」。注意,用戶可以通過向 Awk 發送一個文件結束信號(當使用 Linux 終端窗口時可通過快捷鍵 CTRL-D)來提前退出管道。

不需要循環來處理輸入的重要性是非常值得的。Awk 能夠長期保持存在的一個原因是 Awk 程序是緊湊的,而它們緊湊的一個原因是不需要從控制台或文件中讀取的那些格式代碼。

讓我們運行下面這個程序:

$ awk -f guess.awk
random number is 25
guess a number between 1 and 100: 50
too high, try again: 30
too high, try again: 10
too low, try again: 25
that&apos;s right
$

我們沒有涉及的一件事是注釋。Awk 注釋以 # 開頭,以行尾結束。

總結

Awk 非常強大,這種「猜數字」遊戲是入門的好方法。但這不應該是你探索 Awk 的終點。你可以看看 Awk 和 Gawk(GNU Awk)的歷史,Gawk 是 Awk 的擴展版本,如果你在電腦上運行 Linux,可能會有這個。或者,從它的原始開發者那裡閱讀關於 最初版本 的各種信息。

你還可以 下載我們的備忘單 來幫你記錄下你所學的一切。

Awk 備忘單

via: https://opensource.com/article/21/1/learn-awk

作者:Chris Hermansen 選題:lujun9972 譯者:FYJNEVERFOLLOWS 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國