跳转到内容

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 编程
华夏公益教科书