D语言中的语句(一)

语句

C 和 C++ 程序员将发现 D 的语句非常熟悉,他们还会发现有一些有趣的补充。
	语句:
		标号语句
		语句块
		表达式语句
		声明语句
		If语句
		调试语句
		Version语句
		While语句
		DoWhile语句
		For语句
		Foreach语句
		Switch语句
		Case语句
		Default语句
		Continue语句
		Break语句
		Return语句
		Goto语句
		With语句
		Synchronize语句
		Try语句
		Throw语句
		Volatile语句
		Asm语句
		Pragma语句
	Statement:
		LabeledStatement
		BlockStatement
		ExpressionStatement
		DeclarationStatement
		IfStatement
		DebugStatement
		VersionStatement
		WhileStatement
		DoWhileStatement
		ForStatement
		ForeachStatement
		SwitchStatement
		CaseStatement
		DefaultStatement
		ContinueStatement
		BreakStatement
		ReturnStatement
		GotoStatement
		WithStatement
		SynchronizeStatement
		TryStatement
		ThrowStatement
		VolatileStatement
		AsmStatement
		PragmaStatement

标号语句

语句可以有标号。标号是一种标志符,其后跟随一条语句。
	标号语句:
		标志符 ':' 语句
	LabelledStatement:
		Identifier ':' Statement
	
包括空语句在内的任何语句都可以有标号,因此都可以作为 goto 语句的目标。标号语句也可以用作 break 或者 continue 语句的目标。

标号位于独立的名字空间中,不与声明、变量、类型等位于同一名字空间。就算如此,标号也不能同局部声明重名。标号的名字空间是它们所在的函数体。标号的名字空间不嵌套,也就是说,一个语句块中的标号可以在语句块外访问。


语句块

语句块是由{ }包括起来的语句序列。其中的语句按照词法顺序执行。
	语句块:
		{ }
		{ 语句列表 }

	语句列表:
		语句
		语句 语句列表
	BlockStatement:
		{ }
		{ StatementList }

	StatementList:
		Statement
		Statement StatementList
	
语句块为局部符号引入了新的作用域。尽管如此,在函数内部,局部符号的名称必须唯一。
	void func1(int x)
	{   int x;	// 非法,x 在函数作用域内定义了多次
	}

	void func2()
	{
	    int x;

	    {	int x;	// 非法,x 在函数作用域内定义了多次
	    }
	}

	void func3()
	{
	    {	int x;
	    }
	    {	int x;	// 非法,x 在函数作用域内定义了多次
	    }
	}

	void func4()
	{
	    {	int x;
	    }
	    {	x++;	// 非法,x 未定义
	    }
	}
	
主要的思想是避免在复杂函数内由于作用域声明而错误的覆盖了前面的名称造成 bug 。函数内部的所有名称都必须唯一。

表达式语句

表达式将被计算。
	表达式语句:
		表达式 ;
	ExpressionStatement:
		Expression ;
	
没有作用的表达式,如 (x + x),在表达式语句中是非法的。

声明语句

声明语句声明并初始化变量。
	声明语句:
		类型 标志符列表 ;

	标志符列表:
		变量
		变量 , 标志符列表

	变量:
		标志符
		标志符 = 赋值表达式
	DeclarationStatement:
		Type IdentifierList ;

	IdentifierList:
		Variable
		Variable , IdentifierList

	Variable:
		Identifier
		Identifier = AssignmentExpression
	
如果没有指明 赋值表达式 来初始化变量,变量将被初始化为它的默认值。

If 语句

If 语句提供了按条件执行语句的方法。
	If语句:
		if ( 表达式 ) 语句
		if ( 表达式 ) 语句 else 语句
	
表达式 将被计算,计算的结果必须可以被转换为布尔型。如果为 true 执行 if 之后的语句,否则执行 else 之后的语句。

所谓的‘悬挂 else’问题可以通过邻近匹配原则解决。


While 语句

While 语句实现了简单的循环结构。
	While语句:
		while ( 表达式 ) 语句
	WhileStatement:
		while ( Expression ) Statement
	
表达式 将被计算,计算的结果必须可以被转换为布尔型。如果为 true 执行 语句 。在 语句 被执行后,将再次计算 表达式 ,如果为 true 则再次执行执行 语句 。持续这个过程直到 表达式 结果为 false 。

break 语句将退出循环。continue 语句将直接跳转到下一次计算 表达式


Do-While 语句

Do-While 语句实现了简单的循环结构。
	Do语句:
		do 语句  while ( 表达式 )
	DoStatement:
		do Statement  while ( Expression )
	
表达式 首先被执行。然后计算 表达式 ,结果必须可以被转换为布尔型。如果结果为 true 继续循环,直到 表达式 的值变为 false 。

break 语句将推出循环。continue 语句将直接进行下一轮 表达式 估值。


For 语句

For 语句实现了带有初始化、测试和递增的循环结构。
	For语句:
		for (初始化; 测试; 递增) 语句

	初始化:
		
		表达式
		声明

	测试:
		
		表达式

	递增:
		
		表达式
	ForStatement:
		for (Initialize; Test; Increment) Statement

	Initialize:
		empty
		Expression
		Declaration

	Test:
		empty
		Expression

	Increment:
		empty
		Expression
	
先执行 初始化 。然后计算 测试 ,结果必须可以被转换为布尔型。如果结果为 true ,执行 语句 。在执行 语句 后,将执行 递增 。然后再次计算 测试 ,如果结果为 true 再次执行 语句 。持续这个过程直到 测试 的计算结果为 false 。

break 语句将推出循环。continue 语句将直接跳转到 递增

如果 初始值 声明了一个变量,变量的作用域持续到 语句 结束。例如:

	for (int i = 0; i < 10; i++)
		foo(i);
	
等价于:
	{   int i;
	    for (i = 0; i < 10; i++)
		foo(i);
	}
	
函数体不能为空:
	for (int i = 0; i < 10; i++)
		;	// 非法
	
正确的写法是:
	for (int i = 0; i < 10; i++)
	{
	}
	
初始值 可以忽略。 测试 也可以被忽略,如果忽略,就假定为 true 。

Foreach 语句

foreach 语句遍历一个聚集的全部内容。
	Foreach语句:
		foreach (Foreach类型列表; 表达式) 语句

	Foreach类型列表:
		Foreach类型
		Foreach类型 , Foreach类型列表

	Foreach类型:
		inout 类型 标志符
		类型 标志符
	ForeachStatement:
		foreach (ForeachTypeList; Expression) Statement

	ForeachTypeList:
		ForeachType
		ForeachType , ForeachTypeList

	ForeachType:
		inout Type Identifier
		Type Identifier
	
先计算 表达式 的值。它的结果必须为静态数组、动态数组、关联数组、结构或类类型的聚集表达式。对于聚集表达式的每个元素执行一次 语句 。在每次遍历开始时, Foreach类型列表 声明的变量默认为聚集的内容的拷贝(即采用传值方式)。如果变量是 inout 的,这些变量就是聚集内容的引用(即采用传引用方式)。

如果聚集表达式是静态或动态数组,可以声明一个或者两个变量。如果声明了一个变量,那么这个变量就被赋予数组元素的 ,一个接一个。变量的类型必须同数组内容的类型匹配,除了下面提到的特殊情况以外。如果声明了两个变量,第一个变量对应于 索引 ,第二个对应于 索引 必须是 int 或者 uint 类型,它不能是 inout 的,它的值是数组元素的索引。

	char[] a;
	...
	foreach (int i, char c; a)
	{
	    printf("a[%d] = '%c'/n", i, c);
	}
	
如果聚集表达式是 char、wchar 或者 dchar 的静态或动态数组,则 类型 可以是 char、wchar 或 dchar 中的任何一个。采用这种方式,任何 UTF 数组可以被解码为任何 UTF 类型:
	char[] a = "/xE2/x89/xA0";		// /u2260 编码为 3 个 UTF-8 byte

	foreach (dchar c; a)
	{
	    printf("a[] = %x/n", c);	// 打印 'a[] = 2260'
	}

	dchar[] b = "/u2260";

	foreach (char c; b)
	{
	    printf("%x, ", c);		// 打印 'e2, 89, a0'
	}
	
如果聚集表达式是关联数组,可以声明一个或者两个变量。如果声明了一个变量,那么这个变量就被赋予数组元素的 ,一个接一个。变量的类型必须同数组内容的类型匹配。如果声明了两个变量,第一个变量对应于 索引 ,第二个对应于 索引 必须同关联数组的索引具有相同的类型,它不能是 inout ,它的值是数组元素的索引。
	double[char[]] a;			// 索引 类型是 char[], 类型是 double
	...
	foreach (char[] s, double d; a)
	{
	    printf("a['%.*s'] = %g/n", s, d);
	}
	
如果聚集表达式是一个静态或者动态数组,将从索引 0 到 数组的最大索引 遍历数组元素。如果是关联数组,则元素的顺序是未定义的。如果是一个结构或者一个类对象,访问的顺序由它们的 opApply 成员函数定义。

如果聚集是一个结构或者类对象,则它必须定义具有如下类型的 opApply 函数:

	int opApply(int delegate(inout Type [, ...]) dg);
	
其中 Type 要同 foreach 声明中 标志符 中的 类型 匹配。传递给 opApply 的委托类型中剩下的那些 类型 (上式中的“...”)对应于其余的多个 Foreach类型 。结构或类中可以有多个不同的 opApply 函数, foreach语句 会使用类型匹配的函数,该函数的参数 dg 的参数类型要同 foreach语句 中对应的 Foreach类型 匹配。 opApply 的函数体遍历聚集的元素,并将它们作为参数依次传递给 dg 函数。如果 dg 返回 0 ,则 opApply 继续应用于下一个元素。如果 dg 返回一个非零值,opApply 必须停止遍历并返回那个值。如果 opApply 顺利遍历了所有的元素,会返回 0 。

例如,考虑包含两个元素的容器类:

	class Foo
	{
	    uint array[2];

	    int opApply(int delegate(inout uint) dg)
	    {   int result = 0;

		for (int i = 0; i < array.length; i++)
		{
		    result = dg(array[i]);
		    if (result)
			break;
		}
		return result;
	    }
	}
	
一个可能的例子是:
	void test()
	{
	    Foo a = new Foo();

	    a.array[0] = 73;
	    a.array[1] = 82;

	    foreach (uint u; a)
	    {
		printf("%d/n", u);
	    }
	}
	
将打印出:
	73
	82
	
聚集可以是字符串文字量,这样的聚集可以看作 char、wchar 或 dchar 数组来访问:
	void test()
	{
	    foreach (char c; "ab")
	    {
		printf("'%c'/n", c);
	    }
	    foreach (wchar w; "xy")
	    {
		wprintf("'%c'/n", w);
	    }
	}
	
将打印出:
	'a'
	'b'
	'x'
	'y'
	
inout 用来更新数据项:
	void test()
	{
	    static uint[2] a = [7, 8];

	    foreach (inout uint u; a)
	    {
		u++;
	    }
	    foreach (uint u; a)
	    {
		printf("%d/n", u);
	    }
	}
	
将打印出:
	8
	9
	
在 foreach 遍历所有数据项时,不能修改聚集本身,不能重新设定聚集大小、重新分配、释放、重新赋值或析构。
	int[] a;
	int[] b;
	foreach (int i; a)
	{
	    a = null;		// 错误
	    a.length += 10;	// 错误
	    a = b;		// 错误
	}
	a = null;		// ok
	
如果在 foreach 体内有 Break语句 ,则会退出 foreach ,而 Continue语句 将立即开始下一轮遍历。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值