语 句 2 (2.014)

7 If 语句

If 语句提供了按条件执行语句的方法。

If语句:
if ( If条件 ) Then语句
if ( If条件 ) Then语句 else Else语句

If条件:
表达式
auto 标识符 = 表达式
单个声明符 = 表达式

Then语句:
作用域语句

Else语句:
作用域语句

表达式 将被计算,计算的结果必须可以被转换为布尔型。如果它为 true,则转换到 Then
语句,否则就转换到 Else语句。

“虚悬的(dangling) else”问题可以通过使用最近的那个 if 语句关联到该 else 来解决。

如果提供有一个 auto 标识符,那么它就会被 声明并被初始化 成该表达式的 值和类型。它的作用范围 从被初始化开始一直到 Then语句 的结尾。

如果提供有一个 声明符,那么它就会被声明并被初始化成该表达式的值。它的作用范围从
被初始化开始一直到 Then语句 的结尾。

import std.regexp;
...
if (auto m = std.regexp.search("abcdef", "b(c)d"))
{
writefln("[%s]", m.pre); // 输出 [a]
writefln("[%s]", m.post); // 输出 [ef]
writefln("[%s]", m.match(0)); // 输出 [bcd]
writefln("[%s]", m.match(1)); // 输出 [c]
writefln("[%s]", m.match(2)); // 输出 []
}
else
{
writefln(m.post); // 错误,m 未定义
}
writefln(m.pre); // 错误,m 未定义


8 While 语句

While语句:
while ( 表达式 ) 作用域语句

While 语句实现了简单的循环。[color=red] 表达式 将被计算[/color],计算的结果必须可以被转换为布尔型。
如果它的结果为 true,执行作用域语句。在执行该 作用域语句 后,此 表达式 会被再次计
算,并且如果为 true 的话,该 作用域语句 会被再次执行。这个过程会持续下去,直到 表
达式 的计算出的结果为 false。

int i = 0;
while (i < 10)
{
foo(i);
i++;
}

Break语句 将退出循环。Continue语句 将直接转去再次计算 表达式。


9 Do 语句

Do语句:
do 作用域语句 while ( 表达式 )

do-While 语句实现了简单的循环。 [color=red]先执行 作用域语句。然后 表达式 会被计算[/color],同时要求计算的结果必须可以被转换为 布尔型。如果结果为 true ,则此循环会被重复一次。这个过程会持续下去,直到 表达式 的计算出的 结果为 false。

int i = 0;
do
{
foo(i);
} while (++i < 10);

Break语句 将退出循环。Continue语句 将直接转去再次计算 表达式。


9.10 For 语句

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

For语句:
for (初始化 测试; 递增) 作用域语句

初始化:
;
无范围非空语句

测试:

表达式

递增:

表达式

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

Break语句 将退出循环。Continue语句 将直接跳转到 递增。

A ForStatement creates a new scope.如果 初始值 声明了一个变量,变量的作用域持续到语句结束。

例如:
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++)
{}

初始值 可以忽略。测试 也可以被忽略;[color=red]如果忽略,就假定为 true [/color]。


11 Foreach 语句

foreach 语句遍历一个 聚集 的全部内容。

Foreach语句:
Foreach (Foreach类型列表; 聚集) 作用域语句

Foreach:
foreach
foreach_reverse

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

Foreach类型:
ref 类型 标识符
类型 标识符
ref 标识符
标识符

聚集:
表达式
元组

聚集 会被计算。它的结果必须为 [color=red]静态数组、动态数组、关联数组、结构或类类型的聚集表
达式。[/color]对于聚集表达式的每个元素执行一次 无作用域非空语句。在每次遍历开始
时,Foreach类型列表 声明的变量 默认为聚集的内容的 [color=red]拷贝(即采用传值方式)。[/color]如果变量是 ref,这些变量就是聚集内容的 [color=red]引用(即采用传引用方式)。[/color]

聚集必须是循环不变量,即在 无作用域非空语句 里将聚集的元素不能被添加或移除。

如果聚集表达式是静态或动态数组,可以声明一个或者两个变量。如果声明了一个变量,那
么这个变量就被赋予 数组元素的 值,依着次序地进行。除了下面提到的特殊情况以外,变
量的类型必须同数组内容的类型相 匹配,。

如果声明了两个变量,第一个变量则对应该数组的 [color=red]索引[/color],而第二个对应该数组的 [color=red]值[/color]。注意该 索引 必须是 int 或 uint 类型,而不能是ref,并且它的值就是数组元素的 索引值。

char[] a;
...
foreach (int i, char c; a)
{
writefln("a[%d] = '%c'", i, c);
}

对于 foreach,数组的元素会从索引 0 开始被迭代,一直到该数组的最大值。对于
foreach_reverse,则会以[color=red]相反的顺序[/color]访问数组元素。

如果聚集表达式是 char、wchar 或者 dchar 的静态或动态数组,则 值 的 类型 可以是
char、wchar 或者 dchar 中的任何一个。采用这种方式,所有的 UTF 数组都可以被解码为任意的 UTF 类型:

char[] a = "\xE2\x89\xA0"; // \u2260 encoded as 3 UTF-8 bytes
foreach (dchar c; a)
{
writefln("a[] = %x", c); // 输出 'a[] = 2260'
}
dchar[] b = "\u2260";
foreach (char c; b)
{
writef("%x, ", c); // prints 'e2, 89, a0, '
}

[color=red]聚集可以是字符串文字[/color],这样它们就可以被看作 char、wchar 或 dchar 数组来进行访问:

void test()
{
foreach (char c; "ab")
{
writefln("'%s'", c);
}
foreach (wchar w; "xy")
{
writefln("'%s'", w);
}
}

输出结果:
'a'
'b'
'x'
'y'

如果聚集表达式是关联数组,可以声明一个或者两个变量。如果声明了一个变量,那么这个
变量就被赋予数组元素的 值,依着次序地进行。变量的类型必须同数组内容的类型匹配。

如果声明了两个变量,第一个变量则对应该数组的 索引,而第二个对应该数组的 值。索引
必须同关联数组的索引具有相同的类型。它不能是 ref,它的值是数组元素的索引。对于
foreach,数组元素的顺序是不明确的。而将 foreach_reverse 用于关联数组是非法的。

double[char[]] a; // 索引 类型是 char[],值 类型是 double
...
foreach (char[] s, double d; a)
{
writefln("a['%s'] = %g", s, d);
}

如果它是一个结构或类对象,则 foreach 会通过特有的 opApply 成员函数会定
义。foreach_reverse 的动作则由特有的 opApplyReverse 成员函数进行定义。为了使用相应的 foreach 语句,这些特有函数必须被该类型定义。这些函数拥有的类型:

int opApply(int delegate(ref Type [, ...]) dg);
int opApplyReverse(int delegate(ref Type [, ...]) dg);

这里的 类型 跟在 标识符 里的 Foreach类型 声明所使用的 类型 相匹配。多重 Foreach
类型 跟在传递给 opApply 或 opApplyReverse 的委托类型里的多重 类型 相一致。也可以有多重 opApply 和 opApplyReverse 函数,通过将 dg 的类型跟 Foreach语句 的 Foreach类型 相匹配来进行一个一个的选择。应用函数体会迭代它所聚集的全部元素,并把它们每一个都传递给 dg 函数。如果 dg 返回 0,那么应用就继续下一个元素。如果 dg 返回一个非零值,apply 必须停止迭代并返回该值。否则,在迭代完所有的元素之后,apply 会返回0。

例如,假设有一个类,它是一个窗口,拥有两个元素:

class Foo
{
uint array[2];
int opApply(int delegate(ref 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)
{
writefln("%d", u);
}
}

输出结果:
73
82

如果 聚集 是一个委托,那么此委托的类型署名就跟用于 opApply 的一样。这使得许多不
同命名的循环策略可以在相同的类或结构里共存。

ref 可以被用来更新原有的元素:
void test()
{
static uint[2] a = [7, 8];
foreach (ref uint u; a)
{
u++;
}
foreach (uint u; a)
{
writefln("%d", u);
}
}

输出结果:
89

ref 不能被应用到该索引值。

如果没有指定,在 Foreach类型 里的 类型 可以从 聚集 的类型进行推断。
在 foreach 迭代所有元素时,聚集本身不需要被重新调整大小、重新分配、释放、重新赋值
或重新析构。

int[] a;
int[] b;
foreach (int i; a)
{
a = null; // 错误
a.length = a.length + 10; // 错误
a = b; // 错误
}
a = null; // 正确

如果聚集是一个 tuple,那么就可能会声明一到两个变量。如果声明了一个变量,那么这个
变量就被赋予该 Tupe 元素的 值,一个接一个地。如果给定了该变量的类型,则它必须跟
Tuple 内容里的类型相匹配。如果没有给定,则该变量的类型会被设置成该 Tuple 元素的类型,它可能从一个迭代变化到另一个迭代。如果声明了两个变量,第一个变量则对应该数组
的 索引,而第二个对应该数组的 值。索引 必须是 int 或 uint 类型,它不能是 ref,并且它的值是该 Tuple 元素的索引。

如果该 Tuple 是一个类型列表,那么该 foreach 语句对于每一个类型都会被执行一次,并且该值会成该类型的别名。

import std.stdio;
import std.typetuple; // 用于类型元组
void main()
{
alias TypeTuple!(int, long, double) TL;
foreach (T; TL)
{
writefln(typeid(T));
}
}

输出
int
long
double

如果在 foreach 体内有 Break语句,则会退出 foreach ,而 Continue语句 将立即开始下一轮遍历。


12 Switch 语句

switch 语句依照 switch 表达式的值来选择一条 case 语句执行。

Switch语句:
switch ( 表达式 ) 作用域语句

Case语句:
case 表达式列表 :语句

Default语句:
default: 语句

表达式 会被计算。结果的类型 T 必须是 整数类型 或者 char[]、 wchar[] 或者 dchar[]。所得结果同各个 case 表达式进行比较。如果存在匹配,就转而执行相应的 case 语句。

case 表达式,也就是 表达式列表,是由 逗号分隔 的表达式的列表。

如果没有 case 表达式能够匹配,并且存在 default 语句,就转去执行 default 语句。

如果没有 case 表达式能够匹配,并且不存在 default 语句,就会抛出 SwitchError 异常。之所以这么做,是为了捕获一些常见的错误,如为枚举添加了新值却忘记了为这个值提供对应的 case 语句。[color=red]这同 C 和 C++ 不太一样。[/color]

case 表达式必须都是常量值或数组,
 2.014
or a runtime initialized const or invariant variable of integral type.


并且必须能隐式地转换为 switch 表达式 的类型 T。

case 表达式的值必须互不相同。

  2.014
Const or invariant variables must all have different names. If they share a value, the first case statement with that value gets control.


而且不能有两个或者更多的 default 语句。

与 switch 相关联的 case 语句和 default 语句可以在语句块中嵌套;它们不必非要位于最外层的块中。例如,下面是合法的:

switch (i)
{
case 1:
{
case 2:
}
break;
}

同在 C 和 C++ 中一样,case 语句会顺序执行后面的 case 子句。break 语句将退出 switch块语句。

例如:
switch (i)
{
case 1:
x = 3;
case 2:
x = 4;
break;
case 3,4,5:
x = 5;
break;
}

如果 i 等于 1,x 将等于 4 。
注意: 同 C 和 C++ 不同的是,可以在 switch 表达式中使用字符串。例如:
char[] name;
...
switch (name)
{
case "fred":
case "sally":
...
}

对于如命令行选项处理这样的应用来说,采用这种语法的代码更直接,更清晰并且不易出
错。ascii 和 wchar 都可以使用。

实现注意: 编译器的代码生成程序可以认为 case 语句已经按照使用的频率排序,频率高的
排在最前面,频率低的排在最后。尽管这不会影响程序的正确性,但对于提高性能来说是有
益的。


13 Continue 语句

Continue语句:
continue;
continue 标识符 ;

continue 会中当前断其封闭循环语句的迭代,然后开始下一次迭代。 continue 执行它所在最内层的 while、for 或者do 语句的下次迭代。执行递增子句。

如果 continue 后跟有 标识符,则该 标识符 必须是它所在的 while、for 或者 do 语句外的标号,continue 会执行所在循环的下次迭代。如果不存在那样的语句,就是错误。
所有被跳过的 finally 子句都会执行,同时会释放所有被跳过的 synchronization 对象。

注意: 如果 finally 子句中有 return、throw 或者 goto 到 finally 子句之外的这样的动作,continue 的目标永远也无法达到。

for (i = 0; i < 10; i++)
{
if (foo(i))
continue;
bar();
}


14 Break 语句

Break语句:
break;
break 标识符 ;

break 退出它所在的语句。 break 退出它所在最内层的 while、for、do 或者 switch 语句,并在该语句之后恢复执行。

如果 break 后跟有 标识符,则该 标识符 是它所在的 while、for、do 或者 switch 语句外的标号,break 会退出那条语句。如果不存在那样的语句,就是错误。

所有被跳过的 finally 子句都会执行,同时会释放所有被跳过的 synchronization 对象。

注意: 如果 finally 子句中有 return、throw 或者 goto 到 finally 子句之外的这样的动作,break 的目标永远也无法达到。

for (i = 0; i < 10; i++)
{
if (foo(i))
break;
}


15 Return 语句

Return语句:
return;
return 表达式 ;

return 语句的作用是推出当前的函数并提供一个返回值。 如果函数指定了非 void 的返回类型,就必须给出 表达式。表达式 会被隐式地转换为函数的返回类型。

如果函数指定了一个不为 void 的返回类型,则至少需要一个 return 语句、throw 语句 或assert(0) 表达式。

就算函数的返回类形是 void,也可以有 表达式 存在。表达式 会被计算,但是什么也不会
返回。

[color=red]在函数实际返回之前,任何带有 auto 存储特性的对象都会被破坏掉,所有的封闭 finally 子句会被执行,所有的 scope(exit) 语句会被执行,所有的 scope(success) 语句会被执行,并且所有的封闭 synchronization 对象会被释放。[/color]

如果外围的 finally 子句中有 return、goto 或者 throw 的话,函数就不会正常的返回。

如果存在后验条件(参见契约式编程),则在计算 表达式 之后执行该后验条件,然后函数
返回。

int foo(int x)
{
return x + 3;
}


16 goto 语句

Goto语句:
goto 标识符 ;
goto default ;
goto case ;
goto case 表达式 ;

一个 goto 会跳转到以 标识符 为标号的语句处。

if (foo)
goto L1;
x = 3;
L1:
x++;

第二种形式:goto default;,即跳转到最内层 Switch语句 中的 Default语句 处。

第三种形式:goto case;,即跳转到最内层 Switch语句 中的下一个 Case语句 处。

第四种形式:goto case 表式;,即跳转到跟封闭在 Switch语句 里面的匹配该
Expression 的 Case语句 处。

switch (x)
{
case 3:
goto case;
case 4:
goto default;
case 5:
goto case 4;
default:
x = 4;
break;
}

所有的被跳过的 finally 子句都会执行,同时会释放所有的被跳过的同步互斥体。
使用 Goto语句 来跳过初始化是非法的。


17 With 语句

with 语句用来简化对同一个对象的重复引用。

With语句:
with ( 表达式 ) 作用域语句
with ( 符号 ) 作用域语句
with ( 模板实例 ) 作用域语句

表达式 的结果是对类实例或者结构的引用。在 with 的过程体内,所有的标志符符号都将在
那个类引用的名字空间内查找。With语句

with (expression)
{
...
ident;
}

在语义上等价于:
{
Object tmp;
tmp = expression;

...
tmp.ident;
}

注意 表式 只会计算一次。with 语句不会改变 this 或 super 引用的对象。

对于是一个作用域或者 模板实例 的符号,在查找符号时相应的作用域会被搜索。

例如:
struct Foo
{
typedef int Y;
}
...
Y y; // 错误,Y 未定义
with (Foo)
{
Y y; // 等同于 Foo.Y y
}


。。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值