通過寫“猜數字”游戲學習 Fortran
- Fortran 是在打孔卡時代編寫的語言,因此它的語法非常有限。但你仍然可以用它編寫有用和有趣的程序。
Fortran 77 是我學習的第一門編譯型編程語言。一開始時,我自學了如何在 Apple II 上用 BASIC 編寫程序,后來又學會在 DOS 上用 QBasic 編寫程序。但是當我去大學攻讀物理學時,我又學習了 Fortran。
Fortran 曾經在科學計算中很常見。曾幾何時,所有計算機系統都有一個 Fortran 編譯器。Fortran 曾經像今天的 Python 一樣無處不在。因此,如果你是像我這樣的物理學專業學生,在 1990 年代工作,那你肯定學習了 Fortran。
我一直認為 Fortran 與 BASIC 有點相似,所以每當我需要編寫一個簡短程序,來分析實驗室數據或執行其他一些數值分析時,我都會很快想到 Fortran。我在空閑時用 Fortran 編寫了一個“猜數字”游戲,其中計算機會在 1 到 100 之間選擇一個數字,并讓我猜這個數字。程序會一直循環,直到我猜對了為止。
“猜數字”程序練習了編程語言中的幾個概念:如何為變量賦值、如何編寫語句以及如何執行條件判斷和循環。這是學習新編程語言時一個很好的的實踐案例。
Fortran 編程基礎
雖然 Fortran 這些年來一直在更新,但我最熟悉的還是 Fortran 77,這是我多年前學習的實現版本。Fortran 是程序員還在打孔卡上編程的年代創建的,因此“經典” Fortran 僅限于處理可以放在打孔卡上的數據。這意味著你只能編寫符合以下限制條件的經典 Fortran 程序(LCTT 譯注:后來的 Fortran 95 等版本已經對這些限制做了很大的改進,如有興趣建議直接學習新版):
- 每張卡只允許一行源代碼。
- 僅識別第 1-72 列(最后八列,73-80,保留給卡片分類器)。
- 行號(“標簽”)位于第 1-5 列。
- 程序語句在第 7-72 列。
- 要表示跨行,請在第 6 列中輸入一個連續字符(通常是 +)。
- 要創建注釋行,請在第 1 列中輸入 C 或 *。
- 只有字符 A 到Z(大寫字母)、0 到9(數字)和特殊字符 = + - * / ( ) , . $ ' : 和空格能夠使用。
雖然有這些限制,你仍然可以編寫非常有用和有趣的程序。
在 Fortran 中猜數字
通過編寫“猜數字”游戲來探索 Fortran。這是我的實現代碼:
- CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
- C PROGRAM TO GUESS A NUMBER 1-100
- CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
- PROGRAM GUESSNUM
- INTEGER SEED, NUMBER, GUESS
- PRINT *, 'ENTER A RANDOM NUMBER SEED'
- READ *, SEED
- CALL SRAND(SEED)
- NUMBER = INT( RAND(0) * 100 + 1 )
- PRINT *, 'GUESS A NUMBER BETWEEN 1 AND 100'
- 10 READ *, GUESS
- IF (GUESS.LT.NUMBER) THEN
- PRINT *, 'TOO LOW'
- ELSE IF (GUESS.GT.NUMBER) THEN
- PRINT *, 'TOO HIGH'
- ENDIF
- IF (GUESS.NE.NUMBER) GOTO 10
- PRINT *, 'THATS RIGHT!'
- END
如果你熟悉其他編程語言,你大概可以通過閱讀源代碼來弄清楚這個程序在做什么。前三行是注釋塊,表示程序的功能。第四行 PROGRAM GUESSNUM 將其標識為一個 程序program,并由最后一行的 END 語句關閉。
定義變量后,程序會提示用戶輸入隨機數種子。Fortran 程序無法從操作系統初始化隨機數生成器,因此你必須始終使用“種子”值和 SRAND 子程序subroutine 啟動隨機數生成器。
Fortran 使用 RAND(0) 函數生成 0 到 0.999…… 之間的隨機數。參數 0 告訴 RAND 函數生成一個隨機數。將此隨機數乘以 100 以生成 0 到 99.999…… 之間的數字,然后加 1 得到 1 到 100.999…… 之間的值。INT 函數將結果截斷為整數;因此,變量 NUMBER 就是一個介于 1 到 100 之間的隨機數。
程序會給出提示,然后進入一個循環。Fortran 不支持更現代的編程語言中可用的 while 或 do-while 循環(LCTT 譯注:Fortran 95 等新版支持,也因此在一定程度上減少了 GOTO 的使用)。相反,你必須使用標簽(行號)和 GOTO 語句來構建自己的循環。這就是 READ 語句有一個行號的原因:你可以在循環末尾使用 GOTO 跳轉到此標簽。
穿孔卡片沒有 <(小于)和 >(大于)符號,因此 Fortran 采用了另一種語法來進行值比較。要測試一個值是否小于另一個值,請使用 .LT.(小于)。要測試一個值是否大于另一個值,請使用 .GT.(大于)。等于和不等于分別是 .EQ. 和 .NE.。
在每次循環中,程序都會驗證用戶的猜測值。如果用戶的猜測值小于隨機數,程序打印 TOO LOW,如果猜測大于隨機數,程序打印 TOO HIGH。循環會一直持續,直到用戶的猜測值等于目標隨機數為止。
當循環退出時,程序打印 THATS RIGHT! 并立即結束運行。
- $ gfortran -Wall -o guess guess.f
- $ ./guess
- ENTER A RANDOM NUMBER SEED
- 93759
- GUESS A NUMBER BETWEEN 1 AND 100
- 50
- TOO LOW
- 80
- TOO HIGH
- 60
- TOO LOW
- 70
- TOO LOW
- 75
- TOO HIGH
- 73
- TOO LOW
- 74
- THATS RIGHT!
每次運行程序時,用戶都需要輸入不同的隨機數種子。如果你總是輸入相同的種子,程序給出的隨機數也會一直不變。
在其他語言中嘗試
在學習一門新的編程語言時,這個“猜數字”游戲是一個很好的入門程序,因為它以非常簡單的方式練習了幾個常見的編程概念。通過用不同的編程語言實現這個簡單的游戲,你可以弄清一些核心概念以及比較每種語言的細節。
你有最喜歡的編程語言嗎?如何用你最喜歡的語言來編寫“猜數字”游戲?跟隨本系列文章來查看你可能感興趣的其他編程語言示例吧。