Go語言:為什麼要編程做那些只需要按一下計算器就能完成的事情呢?

本章學習目標

  • 學會讓計算機執行數學運算
  • 學會聲明變量和常量
  • 瞭解聲明和賦值的區別
  • 學會使用標準庫生成偽隨機數

計算機程序能夠完成許多任務。在本章中,你將編寫程序去解決數學問題。

請考慮這一點

我們為什麼要編寫程序來做那些只需要按一下計算器就能完成的事情呢?

首先,人類的記性通常都不太好,可能無法憑藉自身的記憶力精確地記下光速或者火星沿著軌道繞太陽一週所需的時間,而程序和計算機沒有這個問題。其次,代碼可以保存起來以供之後閱讀,它既是一個計算器也是一份參考說明。最後,程序是可執行文件,人們可以隨時根據自己的需要來共享和修改它。

2.1 執行計算

人們總是希望自己能夠變得更年輕和更苗條,如果你也有同樣的想法,那麼火星應該能滿足你的願望。火星上的一年相當於地球上的687天,而較弱的重力作用則使得同一物體在火星上的重量約佔地球上重量的38%。

Go語言:為什麼要編程做那些只需要按一下計算器就能完成的事情呢?

為了計算本書作者Nathan在火星上的年齡和體重,我們寫下代碼清單2-1所示的小程序。Go跟其他編程語言一樣,提供了+、-、*、/和%等算術操作符,將它們分別用於執行加法、減法、乘法、除法和取模運算。

提示 取模運算符%能夠計算出兩個整數相除所得的餘數。例如,42 % 10的結果為2。

代碼清單2-1 你好,火星:mars.go

<code>// 我的減重程序  ←--- 為人類讀者提供的註釋
package main
import "fmt"
// main是所有程序的起始函數 ←--- 為人類讀者提供的註釋
func main() {
fmt.Print("My weight on the surface of Mars is ")
fmt.Print(149.0 * 0.3783) ←--- 打印56.3667
fmt.Print(" lbs, and I would be ")
fmt.Print(41 * 365 / 687) ←--- 打印21
fmt.Print(" years old.")
}/<code>

注意 雖然代碼清單2-1會以磅為單位顯示體重,但計量單位的選擇對於體重的計算並無影響。無論你使用的是什麼計量單位,在火星上的重量都只相當於在地球上重量的37.83%。

這段代碼的第一行為註釋。當Go在代碼裡面發現雙斜槓//的時候,它會忽略雙斜槓之後直到行尾為止的所有內容。計算機編程的本質就是傳遞信息,好的代碼不僅能夠把程序員的指令傳遞給計算機,還能夠把程序員的意圖傳遞給其他閱讀代碼的人。註釋的存在正是為了幫助人們理解代碼的意圖,它不會對程序的行為產生任何影響。

代碼清單2-1會調用Print函數好幾次,以便將完整的句子顯示在同一行裡面。達到這一目的的另一種方法是調用Println函數,並向它傳遞一組由逗號分隔的參數,這些參數可以是文本、數值或者數學表達式:

<code>fmt.Println("My weight on the surface of Mars is", 149.0*0.3783, "lbs, and I would be", 41*365.2425/687, "years old.")  ←--- 打印出“My weight on the surface of Mars is 56.3667 lbs, and I would be 21.79758733624454 years old.”/<code>

速查2-1

請在Go Playground網站中輸入並運行代碼清單2-1,然後將作者Nathan的年齡(41)以及體重(149.0)替換成你的年齡和重量,看看自己在火星上的年齡和體重是多少?


提示 在修改代碼之後,點擊Go Playground中的Format(格式化)按鈕。這樣Go Playground就會在不改變代碼行為的前提下,自動重新格式化代碼的縮進和空白。

2.2 格式化輸出

使用代碼清單2-2中展示的Printf函數,用戶可以在文本中的任何位置插入給定的值。Printf函數與Println函數同屬一族,但前者對輸出擁有更大的控制權。

代碼清單2-2 Printf:fmt.go

<code>fmt.Printf("My weight on the surface of Mars is %v lbs,", 149.0*0.3783)  ←--- 打印出“My weight on the surface of Mars is 56.3667 lbs,”
fmt.Printf(" and I would be %v years old.\\n", 41*365/687) ←--- 打印出“and I would be 21 years old.”/<code>

與Print和Println不一樣的是,Printf接受的第一個參數總是文本,第二個參數則是表達式,而文本中包含的格式化變量%v則會在之後被替換成表達式的值。

注意 之後的各章將按需介紹更多除 %v之外的其他格式化變量,你也可以通過Go的在線文檔查看完整的格式化變量參考列表。

雖然Println會自動將輸出的內容推進至下一行,但是Printf和Print卻不會那麼做。對於後面這兩個函數,用戶可以通過在文本里面放置換行符\\n來將輸出內容推進至下一行。

如果用戶指定了多個格式化變量,那麼Printf函數將按順序把它們替換成相應的值:

<code>fmt.Printf("My weight on the surface of %v is %v lbs.\\n", "Earth", 149.0)  ←--- 打印出“My weight on the surface of Earth is 149 lbs.”/<code>

Printf除可以在句子的任意位置將格式化變量替換成指定的值之外,還能夠調整文本的對齊位置。例如,用戶可以通過指定帶有寬度的格式化變量%4v,將文本的寬度填充至4個字符。當寬度為正數時,空格將被填充至文本左邊,而當寬度為負數時,空格將被填充至文本右邊:

<code>fmt.Printf("%-15v $%4v\\n", "SpaceX", 94)
fmt.Printf("%-15v $%4v\\n", "Virgin Galactic", 100)/<code>

執行上面這兩行代碼將打印出以下內容:

<code>SpaceX          $  94
Virgin Galactic $ 100/<code>

速查2-2

1.如何才能打印出一個新行?

2.Printf函數在遇到格式化變量%v的時候會產生何種行為?

2.3 常量和變量

代碼清單2-1中的計數器在計算時使用了類似0.3783這樣的字面數值,但並沒有具體說明這些數值所代表的含義,程序員有時候會把這種沒有說明具體含義的字面數字稱之為魔數。通過使用常量和變量併為字面數值賦予描述性的名稱,我們可以有效地減少魔數的存在。

在瞭解過居住在火星對於年齡和體重有何種好處之後,我們接下來要考慮的就是旅行所需消耗的時長。對我們的旅程來說,以光速旅行是最為理想的。因為光在太空的真空環境中會以固定速度傳播,所以相應的計算將會變得較為簡單。與此相反的是,根據行星在繞太陽運行的軌道上所處的位置不同,地球和火星之間的距離將會產生相當大的變化。

代碼清單2-3引入了兩個新的關鍵字const和var,它們分別用於聲明常量和變量。

代碼清單2-3 實現光速旅行:lightspeed.go

<code>// 到達火星需要多長時間?
package main
import "fmt"
func main() {
const lightSpeed = 299792 // km/s
var distance = 56000000 // km
fmt.Println(distance/lightSpeed, "seconds") ←--- 打印出“186 seconds”
distance = 401000000
fmt.Println(distance/lightSpeed, "seconds") ←--- 打印出“1337 seconds”
}/<code>

只要將代碼清單2-3中的代碼錄入Go Playground,然後點擊Run按鈕,我們就可以計算出從地球出發到火星所需的時間了。能夠以光速行進是一件非常便捷的事情,不消一會兒工夫你就能到達目的地,你甚至不會聽到有人抱怨“我們怎麼還沒到?”。

這段代碼的第一次計算通過聲明distance變量併為其賦予初始值56 000 000 km來模擬火星與地球相鄰時的情形,而在進行第二次計算的時候,則通過為distance變量賦予新值401 000 000 km來模擬火星和地球分列太陽兩側時的情形(其中401 000 000 km代表的是火星和地球之間的直線距離)。

注意 lightSpeed常量是不能被修改的,嘗試為其賦予新值將導致Go編譯器報告錯誤:“無法對lightSpeed進行賦值”。


注意 變量必須先聲明後使用。如果尚未使用var關鍵字對變量進行聲明,那麼嘗試向它賦值將導致Go報告錯誤,例如在前面的代碼中執行speed = 16就會這樣。這一限制有助於發現類似於“想要向distance賦值卻鍵入了distence”這樣的問題。


速查2-3

1.儘管SpaceX公司的星際運輸系統因為缺少曲速引擎而無法以光速行進,但它仍然能夠以每小時100 800 km這一可觀的速度駛向火星。如果這個雄心勃勃的公司在2025年1月,也就是地球和火星之間相距96 300 000 km的時候發射宇宙飛船,那麼它需要用多少天才能夠到達火星?請修改代碼清單2-3來計算並回答這一問題。

2.在地球上,一天總共有24小時。如果要在程序中為數字24指定一個描述性的名字,你會用什麼關鍵字?

2.4 走捷徑

雖然訪問火星也許沒有捷徑可走,但Go卻提供了一些能夠讓我們少敲些字的快捷方式。

用戶在聲明變量或者常量的時候,既可以在每一行中單獨聲明一個變量:

<code>var distance = 56000000
var speed = 100800/<code>
<code>var (
distance = 56000000
speed = 100800
)/<code>
<code>var distance, speed = 56000000, 100800/<code>

需要注意的是,為了保證代碼的可讀性,我們在一次聲明一組變量或者在同一行中聲明多個變量之前,應該先考慮這些變量是否相關。

速查2-4

請在只使用一行代碼的情況下,同時聲明每天包含的小時數以及每小時包含的分鐘數。

2.4.2 增量並賦值操作符

有幾種快捷方式可以讓我們在賦值的同時執行一些操作。例如,代碼清單2-4中的最後兩行就是等效的。

代碼清單2-4 賦值操作符:shortcut.go

<code>var weight = 149.0
weight = weight * 0.3783
weight *= 0.3783/<code>

Go為加一操作提供了額外的快捷方式,它們的執行方式如代碼清單2-5所示。

代碼清單2-5 增量操作符

<code>var age = 41
age = age + 1 ←--- 生日快樂!
age += 1
age++/<code>

用戶可以使用count--執行減一操作,或者使用類似於price /= 2這樣簡短的方式執行其他常見的算術運算。

注意 順帶一提的是,Go並不支持++count這種見諸C和Java等語言中的前置增量操作。


速查2-5

請用最簡短的一行代碼實現“從名為weight的變量中減去兩磅”這一操作。

2.5 數字遊戲

讓人類隨意想出一個介於1至10之間的數字是非常容易的,但如果想要讓Go來完成同樣的事情,就需要用到rand包來生成偽隨機數。這些數字之所以被稱為偽隨機數,是因為它們並非真正隨機,只是看上去或多或少像是隨機的而已。

執行代碼清單2-6中的代碼會顯示出兩個1 ~ 10的數字。這個程序會先向Intn函數傳入數字10以返回一個0 ~ 9的偽隨機數,然後把這個數字加一併將其結果賦值給變量num。因為常量無法使用函數調用的結果作為值,所以num被聲明成了變量而不是常量。

注意 如果我們在寫代碼的時候忘記對偽隨機數執行加一操作,那麼程序將返回一個0 ~ 9的數字而不是我們想要的1 ~ 10的數字。這是典型的“差一錯誤”(off-by-one error)的例子,這種錯誤是典型的計算機編程錯誤之一。

代碼清單2-6 隨機數字:rand.go

<code>package main

import (
"fmt"
"math/rand"
)

func main() {
var num = rand.Intn(10) + 1
fmt.Println(num)
num = rand.Intn(10) + 1
fmt.Println(num)
}/<code>

雖然rand包的導入路徑為math/rand,但是我們在調用Intn函數的時候只需要使用包名rand作為前綴即可,不需要使用整個導入路徑。

提示 從原則上講,我們在使用某個包之前必須先通過import關鍵字導入該包,但是貼心的Go Playground也可以在需要的時候自動為我們添加所需的導入路徑。為此,你需要確保Go Playground中的Imports複選框已經處於選中狀態,並點擊Format按鈕。這樣一來,Go Playground就會找出程序正在使用的包,然後更新代碼以添加相應的導入路徑。


注意 因為Go Playground會把每個程序的執行結果都緩存起來,所以即使我們重複執行代碼清單2-6所示的程序,最終也只會得到相同的結果,不過能夠做到這一點已經足以驗證我們的想法了。


速查2-6

地球和火星相鄰時的距離和它們分列太陽兩側時的距離是完全不同的。請編寫一個程序,它能夠隨機地生成一個介於56 000 000 km至401 000 000 km之間的距離。

2.6 小結

  • Print、Println和Printf函數都可以將文本和數值顯示到屏幕上。
  • 通過Printf函數和格式化變量%v,用戶可以將值放置到被顯示文本的任意位置上。
  • const關鍵字聲明的是常量,它們無法被改變。
  • var關鍵字聲明的是變量,它們可以在程序運行的過程中被賦予新值。
  • rand包的導入路徑為math/rand。
  • rand包中的Intn函數可以生成偽隨機數。
  • 到目前為止,我們已經使用了25個Go關鍵字中的5個,它們分別是:package、import、func、const和var。

為了檢驗你是否已經掌握了上述知識,請嘗試完成以下實驗。

實驗:malacandra.go

Malacandra並不遙遠,我們大約只需要28天就可以到達那裡。

——C. S. Lewis,《沉寂的星球》(Out of the Silent Planet)

Malacandra是C. S. Lewis在《太空三部曲》中為火星起的別名。請編寫一個程序,計算在距離為56 000 000 km的情況下,宇宙飛船需要以每小時多少千米的速度飛行才能夠用28天到達Malacandra。

請將你的解答與“習題答案”中給出的參考答案進行對比。


速查2-1答案 這個問題沒有標準答案,程序的具體輸出取決於你輸入的體重和年齡。


速查2-2答案

1.你可以通過在待打印文本的任意位置添加換行符\\n來插入新行,或者直接調用fmt.Println()。

2.格式化變量%v將被替換成用戶在後續參數中指定的值。


速查2-3答案

1.雖然宇宙飛船在實際中不可能只沿著直線行進,但作為一個粗略的估計,它從地球飛行至火星大約需要用39天。以下是進行計算所需修改的代碼:

<code>const hoursPerDay = 24
var speed = 100800    // km/h
var distance = 96300000 // km
fmt.Println(distance/speed/hoursPerDay, "days")/<code>

2.因為在地球上一天經過的小時數不會在程序運行的過程中發生變化,所以我們可以使用const關鍵字來定義它。


速查2-4答案

<code>const hoursPerDay, minutesPerHour = 24, 60/<code>

速查2-5答案

<code>weight -= 2/<code>

速查2-6答案

<code>// 隨機地產生一個從地球到火星的距離(以km為單位)
var distance = rand.Intn(345000001) + 56000000
fmt.Println(distance)/<code>


本文摘自《Go語言趣學指南》

內森·揚曼(Nathan Youngman),羅傑·佩珀(Roger Peppé) 著,黃健宏 譯

Go語言:為什麼要編程做那些只需要按一下計算器就能完成的事情呢?

  • Go語言程序設計教程書籍
  • Go編程語言實戰學習筆記入門書
  • 學習過程充滿樂趣,並能積累豐富的實戰經驗

《Go語言趣學指南》是一本面向Go語言初學者的書,循序漸進地介紹了使用Go語言所必需的知識,展示了非常多生動有趣的例子,並通過提供大量練習來加深讀者對書中所述內容的理解。本書共分8個單元,分別介紹變量、常量、分支和循環等基礎語句,整數、浮點數和字符串等常用類型,類型、函數和方法,數組、切片和映射,結構和接口,指針、nil和錯誤處理方法,併發和狀態保護,並且每個單元都包含相應的章節和單元測試。

《Go語言趣學指南》適合對初學Go語言有不同需求的程序員閱讀。無論是剛開始學習Go語言的新手,還是想要回顧Go語言基礎知識的Go語言使用者,只要是想用Go做開發,無論是開發小型腳本還是大型程序,《Go語言趣學指南》都會非常有幫助。


分享到:


相關文章: