跳轉到內容

Pascal 程式設計/字串

來自華夏公益教科書,開放的書籍,開放的世界

資料型別 string() 用於儲存有限的 char 值序列。它是 array 的特例,但與 array[] of char 不同,資料型別 string() 具有某些優勢,有助於有效使用它。

資料型別 string() 如這裡所示是 ISO 標準 10206 中定義的 *擴充套件* Pascal 擴充套件。由於它在實踐中的高度相關性,本主題已放在本華夏公益教科書的標準 Pascal 部分,緊接在關於 陣列 的章節之後。

Warning 許多編譯器對構成 string 的內容有不同的理解。請參閱 *他們的* 手冊瞭解他們特有的差異。請放心,GPC 支援 string(),如這裡所述。

宣告 string 資料型別總是需要一個 *最大容量*

program stringDemo(output);
type
	address = string(60);
var
	houseAndStreet: address;
begin
	houseAndStreet := '742 Evergreen Trc.';
	writeLn('Send complaints to:');
	writeLn(houseAndStreet);
end.

在單詞 string 之後跟著一個 *正* 整數,用括號括起來。這不是函式呼叫。[fn 1]

如上所述,資料型別 address 的變數只能儲存 *最多* 60 個獨立的 char 值。當然,可以儲存更少,甚至儲存 0,但一旦設定了此限制,就無法擴充套件。

String 變數“知道”它們自己的最大容量:如果您使用 writeLn(houseAndStreet.capacity),這將列印 60。每個 string 變數自動具有一個名為 capacity 的“欄位”。此欄位透過寫入相應的 string 變數的名稱以及用點 (.) 連線的單詞 capacity 來訪問。此欄位是隻讀的:您不能向它賦值。它只能出現在表示式中。

所有 string 變數都有當前 *長度*。這是每個 string 變數當前包含的合法 char 值的總數。要查詢此數字,EP 標準定義了一個名為 length 的新函式

program lengthDemo(output);
type
	domain = string(42);
var
	alphabet: domain;
begin
	alphabet := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
	writeLn(length(alphabet));
end.

length 函式返回一個非負的 integer 值,表示所提供字串的長度。它還接受 char 值。[fn 2] 一個 char 值的長度按定義為 1

可以保證 string 變數的 length 將始終小於或等於其相應的 capacity

相容性

[編輯 | 編輯原始碼]

可以使用 := 運算子複製整個字串值,前提是 LHS 上的變數具有與 RHS 字串表示式相同或更大的 capacity。這與普通 array 的行為不同,它要求維度和大小完全匹配。

program stringAssignmentDemo;
type
	zipcode = string(5);
	stateCode = string(2);
var
	zip: zipcode;
	state: stateCode;
begin
	zip := '12345';
	state := 'QQ';
	
	zip := state; // ✔
	// zip.capacity > state.capacity
	// ↯ state := zip; ✘
end.

只要沒有發生剪下,即由於容量太短而省略的值,賦值就可以了。

值得注意的是,否則字串在內部被視為陣列。[fn 3] 就像一個字元陣列,您可以透過指定方括號內有效的索引來獨立訪問(和更改)每個陣列元素。但是,在索引的有效性方面存在很大差異。在任何時候,您只能指定在範圍 1..length 內的索引。此範圍可能為空,特別是如果 length 當前為 0

標準例程

[編輯 | 編輯原始碼]

除了length 函式EP 還定義了一些其他對字串進行操作的標準函式。

以下函式返回字串。

子字串

[編輯 | 編輯原始碼]

為了獲得 string (或 char)表示式的部分內容,函式 subStr(stringOrCharacter, firstCharacter, count) 返回 stringOrCharacter 的子字串,該子字串具有非負長度 count,從正索引 firstCharacter 開始。重要的是 firstCharacter + count - 1stringOrCharacter 中有效的字元索引,否則該函式將導致錯誤。[fn 4]

讓我們看看它在行動中的樣子

程式碼:

program substringDemo(output);
begin
	writeLn(subStr('GCUACGGAGCUUCGGAGUUAG', 7, 3));
	{ char index:   1  4  7  … }
end.

輸出:

GAG
特別注意 firstCharacter 索引。這裡我們想要提取第三個密碼子。但是,firstCharacter 不僅僅是 2 * 3,而是 2 * 3 + 1。在 string 變數中對字元進行索引從 1 開始。請注意,用於編碼密碼子的複雜實現不會使用 string,而是定義自定義的列舉資料型別

對於string 變數,subStr 函式與指定 myString[firstCharacter..firstCharacter+count] 相同。[fn 5] 顯然,如果 firstCharacter 值是某個複雜的表示式,則應首選 subStr 函式以防止出現任何程式設計錯誤。

但是,這種範圍索引語法不僅可以用作表示式的值,還可以用於覆蓋 string 的部分內容。

程式碼:

program substringOverwriteDemo(output);
var
	m: string(35);
begin
	m := 'supercalifragilisticexpialidocious ';
	m[21..35] := '-yadi-yada-yada';
	writeLn(m);
end.

輸出:

supercalifragilistic-yadi-yada-yada
請注意,第一個賦值包含一個尾部空格。如上所述,您不能使用此語法來更改 string 的長度。

此外,subStr 的第三個引數可以省略:這將簡單地返回給定string 從第二個引數指示的位置開始的剩餘部分[fn 6]

刪除尾部空格

[編輯 | 編輯原始碼]

trim(source) 函式返回 source 的副本,不包含任何尾部空格字元,即 ' '。在LTR 指令碼中,任何右側的空格都被認為是無關緊要的,但在計算中,它們佔用(記憶體)空間。建議在將字串寫入磁碟或其他長期儲存介質或透過網路傳輸之前對其進行修剪。不可否認,在 21 世紀之前,記憶體需求是一個更相關的問題。

子字串的第一次出現

[編輯 | 編輯原始碼]

函式 index(source, pattern)source 中查詢 pattern 的第一次出現,並返回起始索引。來自 pattern 的所有字元都與 source 中返回偏移量的字元匹配

  1 2 3  
模式 X Y X  
  1 2 3  
模式   X Y X  
  1 2 3  
模式 X Y X  
Z Y X Y X Y X
1 2 3 4 5 6 7
index('ZYXYXYX', 'XYX') 返回 3

請注意,要獲得第二次或任何後續出現,您需要使用 source 的適當子字串

因為從數學角度來說,“空字串”無處不在index(characterOrString, '') 始終返回 1。相反,因為任何非空字串都不能出現在空字串中,index('', nonEmptyStringOrCharacter) 始終返回 0,在字串的上下文中,這是一個否則無效的索引。如果 pattern 不出現在 source 中,則返回值。如果 pattern source 更長,則情況始終如此。

運算子

[編輯 | 編輯原始碼]

EP 標準為任何長度的字串(包括單個字元)引入了另一個運算子。 + 運算子連線兩個字串或字元,或任何組合。與算術 + 不同,運算子交換,這意味著運算元的順序很重要。

表示式 結果
'Foo' + 'bar' 'Foobar'
'' + '' ''
'9' + chr(ord('0') + 9) + ' Luftballons' '99 Luftballons'
連線示例

如果你想將資料儲存到某個地方,連線很有用。但是,將連線後的字串提供給像 write/writeLn 這樣的例程,可能會帶來不利:連線,特別是長字串的連線,首先需要分配足夠的記憶體來容納整個結果字串。然後,所有運算元都將複製到它們各自的位置。這需要時間。因此,在 write/writeLn 的情況下,建議(對於非常長的字串)使用它們接受無限數量(逗號分隔)引數的功能。

過程等價物

注意,常見的 LOC

stringVariable := 'xyz' + someStringOrCharacter + ;

等價於

writeStr(stringVariable, 'xyz', someStringOrCharacter, );

後者在你想填充結果或需要一些轉換時特別有用。寫 foo:20(最小寬度為 20 個字元,可能用空格 ' ' 左填充)只能使用 write/writeLn/writeStrWriteStrEP 擴充套件。

GPCFPC 和 Delphi 也附帶了一個函式 concat,執行相同的任務。使用它之前,請閱讀各自編譯器的文件,因為有一些差異,或者只堅持使用標準化+ 運算子。

複雜的比較

[edit | edit source]

本小節中介紹的所有函式都返回一個 Boolean 值。

順序

[edit | edit source]

由於字串中的每個字元都有一個序數值,我們可以考慮一種對它們進行排序的方法。有兩種比較字串的方法

  • 一種方法使用已經介紹過的關係運算符,例如 =><=
  • 另一種方法是使用專用函式,例如 LTGT

它們的差異在於它們對長度不同的字串的處理方式。前者會透過使用空格字元 (' ') 填充 它們來使兩個字串具有相同的長度,而後者只會將它們剪下到最短的長度,但會考慮哪個字串更長(如果有必要)。

函式名稱 含義 運算子
EQ 等於 =
NE 不等於 <>
LT 小於 <
LE 小於或等於 <=
GT 大於 >
GE 大於或等於 >=
字串比較函式和運算子

所有這些函式和運算子都是二元的,這意味著它們分別期望和接受兩個引數或運算元。如果提供相同的輸入,它們可能會產生不同的結果,你將在接下來的兩個小節中看到。

相等性

[edit | edit source]

讓我們從相等性開始。

  • 如果兩個運算元的長度相同,並且值,即構成字串的字元序列相同,則 EQ 函式認為兩個字串(任何長度)是相等的。
  • 另一方面,= 比較會透過使用填充字元空格 (' ') 來增加較短字串中任何“缺失”的字元。[fn 7]
讓我們看看實際情況

程式碼:

program equalDemo(output);
const
	emptyString = '';
	blankString = ' ';
begin
	writeLn(emptyString = blankString);
	writeLn(EQ(emptyString, blankString));
end.

輸出:

  True
 False
正如你所看到的,emptyString 被填充以匹配 blankString 的長度,然後執行實際的逐字元 = 表示式。

換句話說,Pascal 中你已經知道的術語

(foo = bar)  =  EQ(trim(foo), trim(bar))

實際實現通常不同,因為 trim 可能非常消耗資源(時間和記憶體),特別是對於長字串而言。

因此,如果尾隨空格無關緊要,但出於技術原因仍然存在(例如,因為你正在使用 array[1..8] of char),通常使用 = 比較。只有 EQ 才能確保兩個字串在詞法上相同。請注意,任一字串的 capacity 與此無關。函式 NE不等於的縮寫)的行為也類似。

小於

[edit | edit source]

透過依次從左到右同時讀取兩個字串,並比較相應的字元,可以確定一個字串“小於”另一個字串。如果所有字元都匹配,則這兩個字串被認為是相等的。但是,如果我們遇到一個不同的字元對,則中止處理,當前字元的關係決定整個字串的關係。

第一個運算元 'A' 'B' 'C' 'D'
第二個運算元 'A' 'B' 'E' 'A'
確定的關係 = = <
'ABCD' < 'ABEA' 計算結果為 true

如果兩個字串的長度相等,則 LT 函式和 < 運算子的行為相同。 LT 實際上甚至建立在 < 的基礎上。如果提供的字串的長度不同,事情就會變得有趣。

  1. LT 函式首先將兩個字串都剪下到相同的(較短的)長度。(子字串
  2. 然後執行常規比較,如上所述。如果縮短的版本,公共長度的版本結果是相等的,則(最初)較長的字串被認為大於另一個字串。
另一方面,< 比較將所有剩餘的“缺失”字元與空格字元 ' ' 進行比較。這會導致不同的結果。

程式碼:

program lessThanDemo(output);
var
	hogwash, malarky: string(8);
begin
	{ ensure ' ' is not chr(0) or maxChar }
	if not (' ' in [chr(1)..pred(maxChar)]) then
	begin
		writeLn('Character set presumptions not met.');
		halt; { EP procedure immediately terminating the program }
	end;
	
	hogwash := '123';
	
	malarky := hogwash + chr(0);
	writeLn(hogwash < malarky, LT(hogwash, malarky));
	
	malarky := hogwash + '4';
	writeLn(hogwash < malarky, LT(hogwash, malarky));
	
	malarky := hogwash + maxChar;
	writeLn(hogwash < malarky, LT(hogwash, malarky));
end.

輸出:

 False  True
  True  True
  True  True
在進行原始 < 比較時, hogwash 中的“缺失”第四個字元被假定為 ' ' 。 malarky 中的第四個字元與 ' ' 進行比較。

上面的情況是出於演示目的而人為造成的,但如果您經常使用比普通空格字元“小”的字元,例如您在使用 ATASCII 的 1980 年代 8 位 Atari 計算機上程式設計,則這仍然會成為問題。 LE 、 GT 和 GE 函式將相應地執行。

有關 string 文字的詳細資訊

[edit | edit source]

包含分隔符

[edit | edit source]

在 Pascal 中, string 文字以相同字元開頭並以相同字元結束。通常情況下,這個字元是直引號(打字機的) ' 。如果您想在 string 文字中實際包含該字元,就會出現問題,因為您要包含在字串中的字元已被理解為結束分隔符。按照慣例,兩個連續的直引號被視為引號影像。在生成的計算機程式中,它們將被替換為單個引號。

program apostropheDemo(output);
var
	c: char;
begin
	for c := '0' to '9' do
	begin
		writeLn('ord(''', c, ''') = ', ord(c));
	end;
end.

每個雙引號將被替換為單個引號。字串仍然需要分隔引號,因此您最終可能得到三個連續的引號,如上面的示例所示,甚至可能得到四個連續的引號( '''' ),如果您想要一個由單個引號組成的 char 值。

不允許的字元

[edit | edit source]

一個 string 是字元的線性序列,即沿單個維度排列。

儘管如此,您仍然可以根據 OS 使用指示 EOL 的特定程式碼,但唯一跨平臺(即保證無論使用的是哪種 OS 都將起作用)的過程是 writeLn 。雖然沒有標準化,但許多編譯器提供一個常量來表示環境中產生換行符所需的字元/字串。在 FPC 中,它被稱為 lineEnding 。Delphi 有 sLineBreak ,出於相容性原因, FPC 也支援它。 GPC 的標準模組 GPC 提供常量 lineBreak 。您需要先 import 此模組,然後才能使用該識別符號。

餘數運算子

[edit | edit source]

在學習了 除法 之後,您將接觸到的最後一個標準 Pascal 算術運算子是餘數運算子 mod (模數的縮寫)。每個 integer 除法( div )可能會產生餘數。此運算子將評估為該值。

i -3 -2 -1 0 1 2 3
i mod 2 1 0 1 0 1 0 1
i mod 3 0 1 2 0 1 2 0
mod 運算示例值

與所有其他除法運算一樣, mod 運算子不接受值作為第二個運算元。此外, mod 的第二個運算元必須正數。在計算機科學家和數學家之間,關於除數為負數時結果的定義有很多。Pascal 透過簡單地宣告負除數是非法的來避免任何混淆。

 mod 運算子經常用於確保某個值保持在從零開始的特定範圍內( 0..n )。此外,您還將在 數論 中找到模數。例如,素數的定義是“不能被任何其他數字整除”。此表示式可以用 Pascal 翻譯成這樣

表示式 可以被  整除
數學符號
Pascal 表示式 x mod d = 0
 mod 的數學關係
Note  odd(x) 是 x mod 2 <> 0 的簡寫形式。[fn 8]

任務

[edit | edit source]
如何從 array[n..m] of string(c) 中訪問單個字元?
由於 string() 本質上是 array 的一種特殊情況(即由 char 值組成的陣列),因此您可以像往常一樣訪問其中的單個字元:v[i, p],其中 i 是範圍 n..m 中的有效索引,而 p 指的是 1..length(v[i]) 內的字元索引。
由於 string() 本質上是 array 的一種特殊情況(即由 char 值組成的陣列),因此您可以像往常一樣訪問其中的單個字元:v[i, p],其中 i 是範圍 n..m 中的有效索引,而 p 指的是 1..length(v[i]) 內的字元索引。


編寫一個布林函式,當且僅當給定的 string() 包含非空白字元(即除 ' ' 以外的字元)時返回 true
以下是一個相當簡潔的實現。
program spaceTest(input, output);
type
	info = string(20);

{**
	\brief determines whether a string contains non-space characters
	\param s the string to inspect
	\return true if there are any characters other than ' '
*}
function containsNonBlanks(s: info): Boolean;
begin
	containsNonBlanks := length(trim(s)) > 0;
end;

// … remaining code for testing purposes only …

注意,此函式(正確地)在提供空字串 ('') 時返回 false。或者您可以編寫

	containsNonBlanks := '' <> s;
但是,這需要一些 Pascal 愛好者(比如你),因為在 *其他* 程式語言中,這種表示式只會檢查 *空* 字串。此外,它需要 string() 資料型別才能正常工作。請記住,在這些練習中沒有“最佳”解決方案。
以下是一個相當簡潔的實現。
program spaceTest(input, output);
type
	info = string(20);

{**
	\brief determines whether a string contains non-space characters
	\param s the string to inspect
	\return true if there are any characters other than ' '
*}
function containsNonBlanks(s: info): Boolean;
begin
	containsNonBlanks := length(trim(s)) > 0;
end;

// … remaining code for testing purposes only …

注意,此函式(正確地)在提供空字串 ('') 時返回 false。或者您可以編寫

	containsNonBlanks := '' <> s;
但是,這需要一些 Pascal 愛好者(比如你),因為在 *其他* 程式語言中,這種表示式只會檢查 *空* 字串。此外,它需要 string() 資料型別才能正常工作。請記住,在這些練習中沒有“最佳”解決方案。


編寫一個 program,它讀取一個 string(),並將其中的每個字母相對於其在英文字母表中的位置移動 13 位,然後輸出修改後的版本。此演算法稱為“凱撒密碼”。為簡單起見,假設所有輸入都是小寫。
此實現利用了您在本單元中看到的多個內容
program rotate13(input, output);
const
	// we will only operate ("rotate") on these characters
	alphabet = 'abcdefghijklmnopqrstuvwxyz';
	offset = 13;
type
	integerNonNegative = 0..maxInt;
	sentence = string(80);
var
	secret: sentence;
	i, p: integerNonNegative;
begin
	readLn(secret);
	
	for i := 1 to length(secret) do
	begin
		// is current character in alphabet?
		p := index(alphabet, secret[i]);
		// if so, rotate
		if p > 0 then
		begin
			// The `+ 1` in the end ensures that p
			// in the following expression `alphabet[p]`
			// is indeed always a valid index (i.e. not zero).
			p := (p - 1 + offset) mod length(alphabet) + 1;
			
			secret[i] := alphabet[p];
		end;
	end;
	
	writeLn(secret);
end.
使用轉換表 (array[chr(0)..maxChar] of char) 的實現也是可以接受的,但必須注意正確填充它。注意,不能保證像 succ('A', 13) 這樣的表示式會產生預期結果。範圍 'A'..'Z' 不一定是連續的,因此您不應對其進行任何假設。如果您的解決方案使用了它,您必須對其進行 *記錄*(例如,“此程式僅在使用 ASCII 字元集 的計算機上正常執行。”)。
此實現利用了您在本單元中看到的多個內容
program rotate13(input, output);
const
	// we will only operate ("rotate") on these characters
	alphabet = 'abcdefghijklmnopqrstuvwxyz';
	offset = 13;
type
	integerNonNegative = 0..maxInt;
	sentence = string(80);
var
	secret: sentence;
	i, p: integerNonNegative;
begin
	readLn(secret);
	
	for i := 1 to length(secret) do
	begin
		// is current character in alphabet?
		p := index(alphabet, secret[i]);
		// if so, rotate
		if p > 0 then
		begin
			// The `+ 1` in the end ensures that p
			// in the following expression `alphabet[p]`
			// is indeed always a valid index (i.e. not zero).
			p := (p - 1 + offset) mod length(alphabet) + 1;
			
			secret[i] := alphabet[p];
		end;
	end;
	
	writeLn(secret);
end.
使用轉換表 (array[chr(0)..maxChar] of char) 的實現也是可以接受的,但必須注意正確填充它。注意,不能保證像 succ('A', 13) 這樣的表示式會產生預期結果。範圍 'A'..'Z' 不一定是連續的,因此您不應對其進行任何假設。如果您的解決方案使用了它,您必須對其進行 *記錄*(例如,“此程式僅在使用 ASCII 字元集 的計算機上正常執行。”)。


編寫一個布林函式,用於確定一個 string 是否是 迴文,這意味著它可以正向 *和* 反向讀取,產生相同的含義/聲音,前提是單詞間隙(空格)經過相應調整。為簡單起見,假設所有字元都是小寫,並且沒有標點符號(除了空格)。
以下是 *一種* 可接受的實現
program palindromes(input, output);

type
	sentence = string(80);

{
	\brief determines whether a lower-case sentence is a palindrome
	\param original the sentence to inspect
	\return true iff \param original can be read forward and backward
}
function isPalindrome(original: sentence): Boolean;
var
	readIndex, writeIndex: integer;
	derivative: sentence;
	check: Boolean;
begin
	check := true;
	
	// “sentences” that have a length of one, or even zero characters
	// are always palindromes
	if length(original) > 1 then
	begin
		// ensure `derivative` has the same length as `original`
		derivative := original;
		// the contents are irrelevant, alternatively [in EP] you could’ve used
		//writeStr(derivative, '':length(original));
		// which would’ve saved us the “fill the rest with blanks” step below
		
		writeIndex := 1;
		// strip blanks
		for readIndex := 1 to length(original) do
		begin
			// only copy significant characters
			if not (original[readIndex] in [' ']) then
			begin
				derivative[writeIndex] := original[readIndex];
				writeIndex := writeIndex + 1;
			end;
		end;
		
		// fill the rest with blanks
		for writeIndex := writeIndex to length(derivative) do
		begin
			derivative[writeIndex] := ' ';
		end;
		// remove trailing blanks and thus shorten length
		derivative := trim(derivative);
		
		for readIndex := 1 to length(derivative) div 2 do
		begin
			check := check and (derivative[readIndex] =
				derivative[length(derivative) - readIndex + 1]);
		end;
	end;
	
	isPalindrome := check;
end;

var
	mystery: sentence;
begin
	writeLn('Enter a sentence that is possibly a palindrome (no caps):');
	readLn(mystery);
	writeLn('The sentence you have entered is a palindrome: ',
		isPalindrome(mystery));
end.
確保您已經理解了只能透過直接“完整”賦值來設定字串的長度(參考 § 索引)。請注意,此實現如何使用 original string 的修改後的 *過濾* 版本。為了演示目的,示例顯示了 if not (original[readIndex] in [' ']) then。實際上,*顯式* 集合列表會更合適,即 if original[readIndex] in ['a', 'b', 'c', , 'z']) then。如果您只是編寫了類似於 if original[readIndex] <> ' ' then 的內容,請不要擔心,鑑於任務的要求,這同樣很好。
以下是 *一種* 可接受的實現
program palindromes(input, output);

type
	sentence = string(80);

{
	\brief determines whether a lower-case sentence is a palindrome
	\param original the sentence to inspect
	\return true iff \param original can be read forward and backward
}
function isPalindrome(original: sentence): Boolean;
var
	readIndex, writeIndex: integer;
	derivative: sentence;
	check: Boolean;
begin
	check := true;
	
	// “sentences” that have a length of one, or even zero characters
	// are always palindromes
	if length(original) > 1 then
	begin
		// ensure `derivative` has the same length as `original`
		derivative := original;
		// the contents are irrelevant, alternatively [in EP] you could’ve used
		//writeStr(derivative, '':length(original));
		// which would’ve saved us the “fill the rest with blanks” step below
		
		writeIndex := 1;
		// strip blanks
		for readIndex := 1 to length(original) do
		begin
			// only copy significant characters
			if not (original[readIndex] in [' ']) then
			begin
				derivative[writeIndex] := original[readIndex];
				writeIndex := writeIndex + 1;
			end;
		end;
		
		// fill the rest with blanks
		for writeIndex := writeIndex to length(derivative) do
		begin
			derivative[writeIndex] := ' ';
		end;
		// remove trailing blanks and thus shorten length
		derivative := trim(derivative);
		
		for readIndex := 1 to length(derivative) div 2 do
		begin
			check := check and (derivative[readIndex] =
				derivative[length(derivative) - readIndex + 1]);
		end;
	end;
	
	isPalindrome := check;
end;

var
	mystery: sentence;
begin
	writeLn('Enter a sentence that is possibly a palindrome (no caps):');
	readLn(mystery);
	writeLn('The sentence you have entered is a palindrome: ',
		isPalindrome(mystery));
end.
確保您已經理解了只能透過直接“完整”賦值來設定字串的長度(參考 § 索引)。請注意,此實現如何使用 original string 的修改後的 *過濾* 版本。為了演示目的,示例顯示了 if not (original[readIndex] in [' ']) then。實際上,*顯式* 集合列表會更合適,即 if original[readIndex] in ['a', 'b', 'c', , 'z']) then。如果您只是編寫了類似於 if original[readIndex] <> ' ' then 的內容,請不要擔心,鑑於任務的要求,這同樣很好。


在不嘗試的情況下,LT('', '') 的結果是什麼?
現在您可以嘗試一下了。
現在您可以嘗試一下了。


編寫一個 function,用於確定公曆中的年份是否為 閏年。每四年是一年閏年,但每百年不閏,除非是連續的第四個世紀。
此任務是您剛剛看到的 mod 運算子 的典型示例
{
	\brief determines whether a year is a leap year in Gregorian calendar
	\param x the year to inspect
	\return true, if and only if \param x meets leap year conditions
}
function leapYear(x: integer): Boolean;
begin
	leapYear := (x mod 4 = 0) and (x mod 100 <> 0) or (x mod 400 = 0);
end;
注意,Delphi 和 FPC 相容的 sysUtils unitGPCGPC module 中已經有一個預製好的 function isLeapYear。儘可能重複使用已經可用的程式碼。
此任務是您剛剛看到的 mod 運算子 的典型示例
{
	\brief determines whether a year is a leap year in Gregorian calendar
	\param x the year to inspect
	\return true, if and only if \param x meets leap year conditions
}
function leapYear(x: integer): Boolean;
begin
	leapYear := (x mod 4 = 0) and (x mod 100 <> 0) or (x mod 400 = 0);
end;
注意,Delphi 和 FPC 相容的 sysUtils unitGPCGPC module 中已經有一個預製好的 function isLeapYear。儘可能重複使用已經可用的程式碼。


編寫一個 function 返回年份的閏年屬性後,編寫一個二進位制 function 返回給定月份和年份中的天數。
這是 case 語句 的典型情況。請記住,結果變數必須 *正好* 賦值一次
type
	{ a valid day number in Gregorian calendar month }
	day = 1..31;
	{ a valid month number in Gregorian calendar year }
	month = 1..12;

{
	\brief determines the number of days in a given Gregorian year
	\param m the month whose day number count is requested
	\param y the year (relevant for leap years)
	\return the number of days in a given month and year
}
function daysInMonth(m: month; y: integer): day;
begin
	case m of
		1, 3, 5, 7, 8, 10, 12:
		begin
			daysInMonth := 31;
		end;
		4, 6, 9, 11:
		begin
			daysInMonth := 30;
		end;
		2:
		begin
			daysInMonth := 28 + ord(leapYear(y));
		end;
	end;
end;
注意,Delphi 和 FPC 相容的 dateUtils unit 提供了一個名為 daysInAMonthfunction。強烈建議您重複使用 *它* 而不是您自己的程式碼。
這是 case 語句 的典型情況。請記住,結果變數必須 *正好* 賦值一次
type
	{ a valid day number in Gregorian calendar month }
	day = 1..31;
	{ a valid month number in Gregorian calendar year }
	month = 1..12;

{
	\brief determines the number of days in a given Gregorian year
	\param m the month whose day number count is requested
	\param y the year (relevant for leap years)
	\return the number of days in a given month and year
}
function daysInMonth(m: month; y: integer): day;
begin
	case m of
		1, 3, 5, 7, 8, 10, 12:
		begin
			daysInMonth := 31;
		end;
		4, 6, 9, 11:
		begin
			daysInMonth := 30;
		end;
		2:
		begin
			daysInMonth := 28 + ord(leapYear(y));
		end;
	end;
end;
注意,Delphi 和 FPC 相容的 dateUtils unit 提供了一個名為 daysInAMonthfunction。強烈建議您重複使用 *它* 而不是您自己的程式碼。

更多練習可以在


注意

  1. 實際上,這是對 EP 稱為“模式”的一種區分。 模式 將在本書的擴充套件部分詳細解釋。
  2. 此功能在處理您或其他人可能在某個時候更改的 *常量* 時很有用。根據定義,字面量值 ' ' 是一個 char 值,而 ''(“空字串”)或 '42' 是字串字面量。為了編寫通用程式碼,length 接受所有可能表示 char 值的有限序列的值。
  3. 實際上,定義本質上是 packed array[1..capacity] of char
  4. 這意味著,在 *空* 字串的情況下,*只有* 以下函式呼叫 *可能是* 合法的 subStr('', 1, 0)。不用說,這樣的函式呼叫非常無用。
  5. 在使用此表示法時,字串變數可能不可 *繫結*。
  6. 在空字串或字元的情況下省略第三個引數是不允許的。 subStr('', 1) 是非法的,因為空字串中沒有“字元 1”。同樣,subStr('Z', 1) 也不允許,因為 'Z' 是一個 char 表示式,因此始終長度為 1,使得任何需要“給我剩餘/後續字元的”函式都變得過時了。
  7. 如果您是 GPC 使用者,您需要確保您處於完全相容 EP 的模式,例如透過在命令列中指定 ‑‑extended‑pascal。否則,不會進行填充。根據 ISO 標準 7185,標準(未擴充套件)Pascal 沒有定義任何填充演算法。
  8. odd 的實際實現可能不同。在許多處理器架構中,它通常類似於 x86 指令 and x, 1
下一頁: 記錄 | 上一頁: 陣列
首頁: Pascal 程式設計
華夏公益教科書