報到

報到

學習練習

簡介

格式化:基礎

Common Lisp 有一個稱為 The Printer 的概念,其中包含了所有用來撰寫 Lisp 資料文字表示法的函式。其中一個這樣的函式是 format,它讓程式設計師能依照自己的需求格式化資料。

format 以及廣義上 Lisp Printer 的能力是個很大的主題;這裡只會介紹 format 用法的基礎。

FORMAT 函式

函式format至少接受兩個引數:要輸出到的串流,以及定義要輸出什麼、如何輸出的控制字串。

串流本身是個獨立的概念,會在別處介紹。但目前你只需要知道,輸出是寫入串流,而這個引數有兩個特殊值:t和nil。t表示輸出到標準輸出(實際上是變數*standard-output*所綁定的串流),而nil表示建立一個字串並回傳它。

請注意,format一律會求值為nil,只有在後者、也就是把nil指定為串流的情況下例外。這是進行單純字串內插或字串建構的常見做法。

(format nil "hello world") ; => "hello world"
(format t "hello world")   ; => NIL (but "hello world" is printed to standard output)

格式指示詞

控制字串可以包含「格式指示詞」,用來指示format要輸出什麼,以及如何解讀傳給format的其他參數。某種程度上,控制字串就是一支程式,format會解讀並執行它。所有指示詞都以~開頭,後面接著一個字元(例如~A、~D、~&)。字元的大小寫沒有差別。

大多數格式指示詞只是把傳給format的引數內插到輸出中。這稱為「消耗」該引數。有些指示詞不會消耗引數,有些則消耗超過一個。甚至有一個指示詞能讓你在引數列表中跳來跳去,或略過某些引數。

基本格式化

一個用途非常廣泛的格式指示詞是~A。它會輸出資料的「人類可讀」版本。

(format nil "Value = ~a" 10)                ; => "10"
(format nil "hello ~a" "world")             ; => "hello world"
(format nil "The list is: ~A" (list 1 2 3)) ; => "The list is: (1 2 3)"

另一個實用的格式指示詞是~S。它會輸出資料的「機器可讀」版本。如果你想讓產生的輸出能被當作 Lisp 資料解讀,這會非常有用。對許多東西而言,它的結果看起來和~A一樣,但要注意,以字串為例,輸出會包含引號(被包含在另一個字串中時會加上跳脫),這樣這個值之後就能被讀回成字串參數。

(format nil "~s" 10)            ; => "10"
(format nil "~s" "hello world") ; => "\"hello world\""
(format nil "~s" (list 1 2 3))  ; => "(1 2 3)"

另外兩個非常實用的指示詞是~%和~&。前者輸出一個換行字元,後者則輸出一個「新行」。差別在於~%一律輸出換行,而~&只有在目前不在行首時才會輸出一個換行。這兩者都不會消耗引數。

(format nil "~%new~%lines") ; => "
                                  new
                                  lines"

(format nil "~&new~&lines"  ; => "new
                                  lines"

最後,~~是一個簡單的指示詞,會輸出一個字面上的~。

說明

就像幾乎所有有工作的人一樣,Lisp 外星人 Layla 有時也需要製作報表,讓其他人知道一些資訊。他問你能不能幫他把他手上這些報表要用的資料格式化。

1. 格式化每季的值

Layla 首先需要的是一個函式:format-quarter-value,它接受兩個參數:季度和值,並將它格式化,以便放進報表。它的求值結果應該是一個字串。

(format-quarter-value "last" 3.14) ; => "The value last quarter: 3.14"
(format-quarter-value "this" 0) ; => "The value this quarter: 0"

2. 兩季報表

Layla 謝謝你寫了 format-quarter-value 這個函式,並說在下一個需要的函式裡,它會非常有用。

下一份報表需要印出 2 個季度的季度與值,分成兩行。

此外,輸出必須送到作為第一個引數提供的那個串流。

例如:(format-two-quarters t "last" 3.14 "this" 0) 會在標準輸出上產生下列內容:


The value last quarter: 3.14
The value this quarter: 0

注意它前後的空行。

3. 人類可讀與 Lisp 外星人可讀

Layla 忘了告訴你,Lisp 外星人喜歡用可以直接當成 Lisp 資料讀取的方式來看值。所以他們需要一個新函式,它的輸出要能被讀成 Lisp 資料(一樣要送到指定的串流):

(format-two-quarters-for-reading nil "last" 3.14 "this" 0) ; =>

"(\"The value last quarter: 3.14\" \"The value this quarter: 0\")"
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Common Lisp Exercism

準備好開始 報到 了嗎?

註冊 Exercism,透過 27 個概念92 個練習 和真人引導來學習並精通 Common Lisp,全部免費。