`
hqs7636
  • 浏览: 220964 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

数 组 2.014

阅读更多
[size=small]
1 数组类别

有四种数组(arrays):

int* p;         指向数据的指针
int[3] s;          静态数组
int[ ] a;          动态数组
int[char[ ] ] x;     关联数组

1.1 指针(Pointers)

int* p;

它们是简单的指向数据的指针,等价于 C 语言的指针。

这些指针的用于提供与 C 的接口,以及用于一些特定的系统工作。由于它没有相关联的长度特性,所以对于在编译或运行时进行越界检查这类工作就没有办法

大多数传统的指针用法可以通过使用动态数组、out 和ref 参数以及引用类型来代替。


1.2 静态数组(Static Arrays)
int[3] s;

这个类似于 C 语言的数组。静态数组与众不同的地方就是其 长度 是在编译时 固定 的。

静态数组总的大小不能超过 16Mb 。对于这样大的数组可以使用动态数组来代替。

维数为 0 的静态数组是允许的,只是不会为它分配空间。它可以用作做动态长度的 结构 的
最后一个成员,或者用作 模板扩展 的退化情况(degenerate case)。

1.3 动态数组(Dynamic Arrays)
int[ ] a;

动态数组 由数组 长度 和指向数据的 指针 组成。多个动态数组 可能 共享 数组中的全部或者部分数据。


2 数组声明(Array Declarations)

有两种声明数组的方式:前缀式 和 后缀式。前缀形式是 首选 的方法,尤其对于一些非普通
(non-trivial)类型。

2.1.1 前缀 数组声明

前缀声明出现在被声明的标志符之前,并从右至左读,于是有:

int[ ] a; // int 型动态数组

int[4][3] b; // 含有 3 个元素的数组,每个元素是含 4 个 int 的数组

int[ ][5] c; // 含有 5 个元素的数组,每个元素是 int 的动态数组

int*[ ]*[3] d; // 含有 3 个元素的数组,每个元素是指向含指向 int 的 动态数组 的 指针

int[ ]* e; // 所指元素为动态数组的指针


2.1.2 后缀数组声明

后缀声明出现在被声明的标志符之后,并且从左至右读。下面的每一组内的声明都是等价
的:
// int 型 动态数组
int[ ] a;
int a[ ];

// 含有 3 个元素的数组,每个元素是含 4 个 int 的数组
int[4][3] b;
int[4] b[3];
int b[3][4];

// 含有 5 个元素的数组,每个元素是 int 的动态数组
int[][5] c;
int[ ] c[5];
int c[5][ ];

// 含有 3 个元素的数组,每个元素是指向含指向 int 的动态数组的指针
int*[ ]*[3] d;
int*[ ]* d[3];
int* (*d[3])[ ];

// 所指元素为动态数组的指针
int[ ]* e;
int (*e)[ ];

基本解释: 后缀形式同 C 和 C++ 采用的形式完全一样,提供这种支持会给用惯了这种形
式的程序员提供一条容易的移植的途径。


3 用法(Usage)

对数组的操作可以分为两类——对数组 引用 的操作和对数组 内容 的操作。C 只让操作符影
响该手柄。

在 D 中,这两种操作都有。

数组名其实就是数组的手柄,如 p、s 或者 a :
int* p;
int[3] s;
int[ ] a;
int* q;
int[3] t;
int[ ] b;

p = q; // p 和 q 指向同一个内容。
p = s; // p 指向数组 s 的第一个元素
p = a; // p 指向数组 a 的第一个元素
s = ...; // 错误,因为 s 是一个编译时
          // 静态数组引用。
a = p; // 错误,因为由 p 所指向的数组,其长度
           // 未知
a = s; // 被初始化为指向数组 s
a = b; // a 和 b 指向同一个数组


10.4 分割(Slicing)

分割(Slicing) 一个数组表示的是指定它的一个 子数组。一个数组分割并不会复制数据,它仅仅是该数组的 又一个引用。

例如:
int[10] a; // 声明一个含有 10 个 int 数组
int[ ] b;
b = a[1..3]; // a[1..3] 是含有 2 个元素的数组,它的内容是
// a[1] 和 a[2]
foo(b[1]); // 等同于 foo(0)
a[2] = 3;
foo(b[1]); // 等同于 foo(3)

简写方式 [ ] 代表整个数组。例如,下面那些赋给 b 的值:
int[10] a;
int[ ] b;
b = a;
b = a[ ];
b = a[0 .. a.length];

在语义上都是等价的。

分割不只是在 引用 数组中的 某个部分 时很方便,还可以把 指针 转换 成带有 边界检查 的数组:int* p;
int[ ] b = p[0..8];


5 数组复制(Array Copying)

当分割运算符 作为赋值表达式 左值 出现时,意味着赋值的 目标 是数组的 内容 而不是对数组的 引用。当左值是一个分割,而右值是一个 数组 或者对应类型的 指针 时,就会发生 复制。

int[3] s;
int[3] t;
s[ ] = t; // t[3] 的 3 个元素被复制到 s[3] 中
s[ ] = t[ ]; // t[3] 的 3 个元素被复制到 s[3] 中
s[1..2] = t[0..1]; // 等价于 s[1] = t[0]

s[0..2] = t[1..3]; // 等价于 s[0] = t[1], s[1] = t[2]
s[0..4] = t[0..4]; // 错误,s 中只有 3 个元素
s[0..2] = t; // 错误,左值和右值的长度不同

重叠复制(Overlapping copies)会被视为错误:
s[0..2] = s[1..3]; // 错误,重叠复制
s[1..3] = s[0..2]; // 错误,重叠复制

相对于 C 的串行语义(serial semantics)来说,禁止重叠复制可以让编译器进行更为大胆的并行代码优化。


6 数组赋值(Array Setting)

如果一个分割运算符 作为 赋值表达式 的 左值 出现,并且右值的 类型 同数组元素的 类型相同,那么作为 左值 的数组的内容 将被设置为 右值 的值。

int[3] s;
int* p;
s[ ] = 3; // 等价于 s[0] = 3, s[1] = 3, s[2] = 3
p[0..2] = 3; // 等价于 p[0] = 3, p[1] = 3


7 数组连接(Array Concatenation)

二元运算符“~”是 连接 运算符。它用来连接数组:
int[ ] a;
int[ ] b;
int[ ] c;
a = b ~ c; // 创建一个新的数组,方法就是将
// b 和 c 两个数组连接起来

许多语言重载了“+”运算符完成连接操作。这会产生令人费解的结果:
"10" + 3

它的值等于 13 还是字符串 "103" ?这并不是一目了然的,语言设计者不得不抖擞精神,小心地编写规则来避免 模棱两可 的情况——而这些规则难以 正确实现、容易被忽视、忘记。

让“+”仍然代表相加,而使用另一个运算符来表示数组的连接,则要好得多。

类似地,“~=” 运算符则表示 追加, 例如:

a ~= b; // a 变为 a 和 b 的连接的结果

连接操作总是会为操作数创建一个 复本,即使其中有一个操作数是 长度为 0 的数组也是一
样,因此:

a = b; // a 引用了 b
a = b ~ c[0..0]; // a 引用到 b 的一个复本


10.8 指针运算(Pointer Arithmetic)

int[3] abc; // 含有 3 个 int 型元素的静态数组
int[ ] def = [ 1, 2, 3 ]; // 含有 3 个 int 型元素的动态数组
void dibb(int* array)
{
array[2]; // 跟 *(array + 2) 相同
*(array + 2); // 获得第 3 个元素
}
void diss(int[] array)
{
array[2]; // 正确
*(array + 2); // 错误,数组不是一个指针
}
void ditt(int[3] array)
{
array[2]; // 正确
*(array + 2); // 错误,数组不是一个指针
}


10.9 矩形数组(Rectangular Arrays)

有经验的 FORTRAN 数值计算程序员都知道,对于像矩阵运算(matrix operations)的这样的事情,使用多维“矩形”数组相比通过指向 指针 的指针(这个源自语义“指向数组的指针的数组”)来访问它们要快很多。

例如,D 的语法:
double[ ][ ] matrix;

就将变量 matrix 声明为一个指向数组的 指针 的数组。(动态数组被实现为指向数组数据的指针。)

因为数组可以有不同的大小(可以动态设置大小),所以有时它被称作“锯齿(jagged)”数组。

对于代码优化来说更为糟糕的是,数组的行有时会互相指向!幸运的是,D的静态数组,使用相同的语法,被实现为固定的矩形分布:

double[3][3] matrix;

它声明了一个 3 行 3 列的矩形矩阵,在内存中连续分布。在其他语言中,它被称为多维数
组,并且声明类似于:

double matrix[3,3];


10 数组长度(Array Length)

在静态或动态数组的 [ ] 中,变量 length 被隐式的声明并设定为数组的长度。也可以使用符号“$”。

int[4] foo;
int[ ] bar = foo;
int* p = &foo[0];

// 下面这些表达式是等价的:
bar[ ]
bar[0 .. 4]
bar[0 .. length]
bar[0 .. $]
bar[0 .. bar.length]

p[0 .. length] // 'length' 未定义,因为 p 不是数组
bar[0]+length // 'length' 未定义,因为它不在 [ ] 内
bar[length-1] // 得到数组内的最后一个元素


11 数组特性(Array Properties)

静态数组的特性(properties)有:

.sizeof    返回以字节为单位的数组的大小。
.length    返回数组中元素的个数。对于静态数组来说这是一个定值。 It is of type size_t.
.ptr       返回指向数组第一个元素的指针。
.dup       创建一个同样大小的动态数组并将原数组的内容复制到新数组中。
.idup      创建一个同样大小的动态数组并将原数组的内容复制到新数组中。 The copy is typed as being invariant. D 2.0 only
.reverse   将数组中的元素按原来的逆序排列。返回数组。
.sort      将数组中的元素按照适当的方法排序。返回数组。

动态数组的特性有:

.sizeof    返回动态数组引用的大小,在 32 位平台上是 8。
.length    获得/设置数组中元素的个数。 It is of type size_t.
.ptr       返回指向数组第一个元素的指针。
.dup       创建一个同样大小的动态数组并将原数组的内容复制到新数组中。
.idup      创建一个同样大小的动态数组并将原数组的内容复制到新数组中。 The copy is typed as being invariant. D 2.0 only
.reverse   将数组中的元素按原来的逆序排列。返回数组。
.sort      将数组中的元素按照适当的方法排序。返回数组。

对于工作在类对象数组上的 .sort 特性,该类的定义里必须定义函数:int opCmp(Object)。它用于决定类对象的顺序。

注意:该参数是 Object 类型,而不是该类的类型。

对于工作在结构或联合数组上的 .sort 特性,该类的定义里必须定义函数:int opCmp(S)
或者 int opCmp(S*)。类型 S 是结构或者联合类型。此函数会决定排列次序。

示例:
p.length // 错误,对于指针来说 length 是未知的
s.length // 编译时间常量 3
a.length // 运行时值

p.dup // 错误,长度未知,无法复制

s.dup // 创建一个含有 3 个元素的数组,
// 将元素复制到它内

a.dup // 创建一个含有 a.length 个元素的数组,
// 同时把 a 的元素复制到它里面


11.1 设置动态数组的长度

动态数组的 .length 特性可以设置为运算符“=”的左值:
array.length = 7;

这会造成数组被在适当的位置被重新分配,现有的内容被原封不动的复制到新的数组中。

如果新的数组比原数组 短,将只保留新数组能够容纳的那些内容。

如果新数组比原数组 长,新数组长出的部分将会使用元素的默认初始值填充。

为了达到最高的效率,运行时总是试图适当地调整数组的大小以避免额外的复制工作。如果
数组不是由 new 运算符或者上一个 resize 操作分配的,并且新数组比原数组大,那么运行时将总是进行复制。

这意味着如果有一个数组进行了分割,紧跟着该数组又被重置大小,那么改变大小后的数组
可能会同那个分割有重叠,即:

char[ ] a = new char[20];
char[ ] b = a[0..10];
char[ ] c = a[10..20];
b.length = 15; // 总是会被根据实际情况的重置大小,因为它是位于 a[] 上的一个分割,
// 而 a[ ] 的长度大于 15

b[11] = 'x'; // a[15] 和 c[5] 也受到影响

a.length = 1;
a.length = 20; // 内存分布没有改变

c.length = 12; // 总是会进行复制,因为 c[ ] 不在
// gc 分配的块的开始处

c[5] = 'y'; // 不影响 a[ ] 和 b[ ] 的内容

a.length = 25; // 可能会也可能不会进行复制
a[3] = 'z'; // 可能会也可能不会影响到 b[3] ,它仍然同原来的
// a[3] 有重叠

为保证复制的行为,请使用 .dup 特性 来确保可以被重新调整大小的数组的唯一性。

这些讨论也同样适用于使用 ~ 和 ~= 运算符进行的连接操作。

重置动态数组的大小是一个相对昂贵的操作。所以,尽管下面这个用于填充数组的方法结果
正确:

int[ ] array;
while (1)
{ c = getinput();
if (!c)
break;
array.length = array.length + 1;
array[array.length - 1] = c;
}

它的效率也不会高。有一种更为实际的方法可以尽量减少 重新调整大小操作的 数量

int[ ] array;
array.length = 100; // 估计的大小
for (i = 0; 1; i++)
{ c = getinput();
if (!c)
break;
if (i == array.length)
array.length = array.length * 2;
array[i] = c;
}
array.length = i;

选择一个好的猜测值是一门艺术,但是通常你能找到一个适用于 99% 的情况的值。例如,
当从控制台采集用户输入时——长度不太可能超过 80 。

11.2 函数做为数组特性

如果函数的第一个参数是数组,那么此函数可以被当作该数组的一个特性来进行调用:
int[ ] array;
void foo(int[] a, int x);
foo(array, 3);
array.foo(3); // 表示同样的意思


12 数组边界检查(Array Bounds Checking)

如果使用 小于 0 或 大于等于 数组长度的数 作为数组下标是一个错误。
若下标越界,如果是由运行时检测到,会抛出一个 ArrayBoundsError 异常;
如果是在编译时检测到,会产生一个错误。

程序不应该依赖于数组越界异常检查,例如,下面这个程序的方法是错误的:
try
{
for (i = 0; ; i++)
{
array[i] = 5;
}
}
catch (ArrayBoundsError)
{
// 终止循环
}

此循环的正确写法是:
for (i = 0; i < array.length; i++)
{
array[i] = 5;
}

实现注意: 编译器应该在编译时尝试检查数组越界错误,例如:

int[3] foo;
int x = foo[3]; // 错误,越界了

应该可以在编译时通过一个 选项 决定是否 插入 动态数组越界检查代码。
//参见dmd编译开关


13 数组初始化(Array Initialization)

13.1 默认初始化(Default Initialization)

• 指针被初始化为 null。
• 静态数组的内容被初始化为数组元素类型的默认初始值。
• 动态数组被初始化为拥有 0 个元素。
• 关联数组被初始化为拥有 0 个元素。

13.2 空的初始化(Void Initialization)

当数组的 初始值 为 void 时就会发生空的(void)初始化。即表示没有进行初始化,此时数组的内容将为未定义。

这个做为效率优化是最有用的。空的初始化是一种高级技术,应该只在经剖析表明它有必要时才使用它。

10.13.3 静态数组 的 静态初始化(Static Initialization)

静态初始化由包含在 [  ] 里面的数组元素值列表实现。这些值可以可选地在它们的前面放置一个 索引值 和“:”。如果没有提供索引值,则该索引值会被设置成前一个索引值 加 1,或者 如果是第一个值,则索引值会为 0。

int[3] a = [ 1:2, 3 ]; // a[0] = 0, a[1] = 2, a[2] = 3

当数组下标识为枚举类型时,这种写法是最方便的:

enum Color { red, blue, green };

int value[Color.max + 1] = [ Color.blue:6, Color.green:2, Color.red:5 ];

当这些数组出现在全局域里时,它们都是静态的。否则,就需要使用 const 或 static 存储类别来对它们进行标记,让它们成为静态数组。


14 特殊数组类型

14.1 字符串(Strings)

一个字符串指的就是由多个字符组成的一个数组。而字符串字法指的仅仅是一种写成字符数
组的简单的方法。

字符串文字是不可变的(只读)。

  2.014中没有这一段
char[ ] str;
char[ ] str1 = "abc";
str[0] = 'b'; // 出错,"abc"是只读的,可能导致崩溃


  1.030
The name string is aliased to char[], so the above declarations could be equivalently written as: 

string str;
string str1 = "abc";


  2.014
char[] str1 = "abc";                // error, "abc" is not mutable
char[] str2 = "abc".dup;            // ok, make mutable copy
invariant(char)[] str3 = "abc";     // ok
invariant(char)[] str4 = str1;      // error, str4 is not mutable
invariant(char)[] str5 = str1.idup; // ok, make invariant copy

The name string is aliased to invariant(char)[], so the above declarations could be equivalently written as: 

char[] str1 = "abc";       // error, "abc" is not mutable
char[] str2 = "abc".dup;   // ok, make mutable copy
string str3 = "abc";       // ok
string str4 = str1;        // error, str4 is not mutable
string str5 = str1.idup;   // ok, make invariant copy


(2.014中还是搞复杂了)

char[] 字符串采用 UTF-8 格式。wchar[] 字符串采用 UTF-16 格式。dchar[] 字符串采用 UTF-32 格式。

字符串可以复制、比较、连接和追加:

str1 = str2;
if (str1 < str3) ...
func(str3 ~ str4);
str4 ~= str1;

使用显而易见的语义。所有生成的临时对象都会被垃圾回收器回收(或者使用 alloca())。

而且,这对于所有的数组都适用,而不仅仅是对字符串数组。

可以使用指向 char 的指针:
char* p = &str[3]; // 指向第 4 个元素
char* p = str; // 指向第1 个元素

但是,因为 D 中的字符串数组不是以 0 终止的,所以如果要把一个字符串指针传递给 C
时,应该加上终止符 0 :

str ~= "\0";

或者使用函数 std.string.toStringz。

字符串的类型在编译的语义分析阶段决定。

类型是下列之一:char[ ]、wchar[ ]、dchar[ ] ,并且按照隐式转换规则决定。

如果有两个可用的等价的转换,会被认为是一个错误。为了避免出现这种模棱两可的局面,应该使用类型转换,或者使用后缀 c、w 或 d:

cast(wchar [ ])"abc" // 这是个 wchar 型的字符数组
"abc"w // 这个也是

如果需要的话,字符串文字会隐式地在 char、wchar 和 dchar 之间转换。

char c;
wchar w;
dchar d;
c = 'b'; // c 被赋值为 char 字符 'b'
w = 'b'; // w 被赋值为 wchar 字符 'b'
w = 'bc'; // 错误——只能使用一个 wchar 字符
w = "b"[0]; // w 被赋值为 wchar 字符 'b'
w = \r[0]; // w 被赋值为 wchar 字符“回车”
d = 'd'; // d 被赋值为 dchar 字符 'd'

14.1.1 C 的 printf() 和 字符串

printf( ) 是一个 C 函数,它不是 D 的一部分。printf() 输出以 0 终止的 C 字符串。

有两种用 printf() 输出 D 字符串的方法。

第一种方法是加一个终止符 0 ,并将结果转换为 char* :

str ~= "\0";
printf("the string is '%s'\n", cast(char*)str);

或者:
import std.string;
printf("the string is '%s'\n", std.string.toStringz(str));


字符串文字后面已经添加有一个 0,因此可以直接使用它们:
printf("the string is '%s'\n", cast(char*)"string literal");

因此,为什么传递给 printf 的第一个字符串文字不需要进行类型转换呢?第一个参数被原型化(prototyped)成了一个 char*,而一个字符串文字可以被隐式转换到类型 char*。

但是传递给 printf 的余下实参是 个数 不定型(variadic)的(由“...”指定),而且一个字符串文字是被当作一个(长度,指针)的 组合 传递给 个数 不定型形参的。

第二种方法使用精度指示符。D 数组的布局是:数组长度、字符串,所以下面这种方法可以
工作:

printf("the string is '%.*s'\n", str);

最好的方式是使用 std.stdio.writefln,它可以处理 D 字符串:

import std.stdio;
writefln("the string is '%s'", str);

14.2 隐式转换

指针 T* 可以被隐式的转换为下面的内容:

• void*

静态数组 T[dim] 可以被隐式地转换为下列之一:
• T[ ]
• U[ ]
• void[ ]

动态数组 T[ ] 可以被隐式地转换为下列之一:
• U[ ]
• void[ ]

这里的 U 是 T 的基类。


15 关联数组(Associative Arrays)

关联数组有一个索引,此索引不必是一个整数,而且可以是少量增加(sparsely populated)。一个关联数组的索引叫做 关键字(key),而它的类型叫做 关键字类型(KeyType)。

在关联数组的声明中,关键字类型 的类型声明位于 [ ] 内:

int[char[ ] ] b; // 整型关联数组 b
// 通过一个字符数组来建立索引。
// 其 关键字类型 是 char[]
b["hello"] = 3; // 设定关键字 "hello" 对应的值为 3
func(b["hello"] ); // 把 3 做为参数传递给 func( )
关联数组里的特定关键字可以通过 remove 函数被移除:
b.remove("hello");
如果该关键字在关联数组里,则 In表达式 会产生一个指向此关联字所关联的值的指针;如
果不在,则为 null:
int* p;
p = ("hello" in b);
if (p != null)
...

关键字类型 不能是函数或者 void。

如果 关键字类型 是一个结构类型,则会使用默认机制基于结构值内的二元数据来计算
hash(哈稀值)和它的比较值。也可以通过提供下面函数做为结构成员实现自定义机制:

uint toHash( );
int opCmp(KeyType* s);

例如:
import std.string;

struct MyString
{
char[ ] str;
uint toHash( )
{ uint hash;
foreach (char c; s)
hash = (hash * 9) + c;
return hash;
}
int opCmp(MyString* s)
{
return std.string.cmp(this.str, s.str);
}
}


15.1 使用类做为关键字类型

类也可以被用作 关键字类型。为了这个可以工作,类定义必须重写(override) Object 类的下列成员函数:

• hash_t toHash( )
• int opEquals(Object)
• int opCmp(Object)

注意,opCmp 和 opEquals 的参数都是 Object 类型,而不是定义的那个类类型。

例如:
class Foo
{
int a, b;
hash_t toHash( ) { return a + b; }
int opEquals(Object o)
{ Foo f = cast(Foo) o;
return f && a == foo.a && b == foo.b;
}
int opCmp(Object o)
{ Foo f = cast(Foo) o;
if (!f)
return -1;
if (a == foo.a)
return b - foo.b;
return a - foo.a;
}
}

这些实现可以使用 opEquals 或者 opCmp 或者两者。小心以防 opEquals 和 opCmp 的
结果在它们的类对象不同时彼此混淆,或者小心相反的情况。


15.2 使用结构或联合做为关键字类型

结构或联合也可以被用作 关键字类型。想要它工作,结构或联合定义里必须定义下列成员
函数:

• hash_t toHash()
• int opEquals(S) or int opEquals(S*)
• int opCmp(S) or int opCmp(S*)

注意:opCmp 和 opEquals 的参数可以是结构或联合类型,或者是指向结构或联合类型的
指针。

例如:
struct S
{
int a, b;
hash_t toHash( ) { return a + b; }
int opEquals(S s)
{
return a == s.a && b == s.b;
}
int opCmp(S* s)
{
if (a == s.a)
return b - s.b;
return a - s.a;
}
}

这些实现可以使用 opEquals 或者 opCmp 或者两者。小心以防 opEquals 和 opCmp 的
结果在它们的结构/联合对象不同时彼此混淆,或者小心相反的情况。

15.3 特性

关联数组的特性有:
.sizeof    返回指向关联数组的引用的大小;通常值是 8 。
.length    返回关联数组中值的个数。与动态数组不同的是,它是只读的。
.keys      返回动态数组,数组的元素就是关联数组的那些关键字。
.values    返回动态数组,数组的元素就是关联数组的那些对应值。
.rehash    适当地重新组织关联数组以提高查找效率。例如,程序已经载入了符号表并将开始进行查找事,rehash 会提高效率。返回指向新数组的引用。


15.4 关联数组示例:单词统计
import std.file; // D 文件 I/O
import std.stdio;
int main (char[ ][ ] args)
{
int word_total;
int line_total;
int char_total;
int[char[ ] ] dictionary;
writefln(" lines words bytes file");
for (int i = 1; i < args.length; ++i) // 程序的形式参数
{

char[ ] input; // 输入缓冲区
int w_cnt, l_cnt, c_cnt; // word、line、char 计数器
int inword;
int wstart;

// 将文件读入到 input[ ]
input = cast(char[])std.file.read(args[i]);
foreach (j, char c; input)
{
if (c == '\n')
++l_cnt;
if (c >= '0' && c <= '9')
{
}
else if (c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z')
{
if (!inword)
{
wstart = j;
inword = 1;
++w_cnt;
}
}
else if (inword)
{
char[] word = input[wstart .. j];
dictionary[word]++; // 递增 word 的计数器
inword = 0;
}
++c_cnt;
}
if (inword)
{
char[] word = input[wstart .. input.length];
dictionary[word]++;
}
writefln("%8d%8d%8d %s", l_cnt, w_cnt, c_cnt, args[i]);
line_total += l_cnt;
word_total += w_cnt;
char_total += c_cnt;
}
if (args.length > 2)
{
writef("-------------------------------------\n%8ld%8ld%8ld total",
line_total, word_total, char_total);
}
writefln("-------------------------------------");
foreach (word; dictionary.keys.sort)
{
writefln("%3d %s", dictionary[word], word);
}
return 0;
}


[/size]

。。。。。
分享到:
评论

相关推荐

    魅族音乐播放器升级DPU教程

    E3 2.014版的DPU升级教程将帮助用户了解整个过程。 首先,我们需要理解DPU是什么。DPU,全称为数字处理单元,是设备中负责处理特定任务的微处理器。在魅族音乐播放器中,DPU可能负责音频解码、信号处理等关键功能。...

    Web-programming-languages:Web 编程语言 (CS 6301.014) 作业和项目

    在Web开发领域,JavaScript是一种至关重要的编程语言,它在【标题】"Web-programming-languages:Web 编程语言 (CS 6301.014) 作业和项目"以及【描述】中被特别提及,暗示了课程或资料的重点。JavaScript主要用于构建...

    灾后恢复生产管理程序2.pdf

    首先,标题《灾后恢复生产管理程序2.pdf》和描述“灾后恢复生产管理程序2.pdf”明确指出了该文档的主题是关于灾后生产恢复的管理程序。在IT行业中,灾后恢复(Disaster Recovery,简称DR)是一个关键领域,它涉及到...

    bst-bme680-an014.pdf

    2. 非常低的功耗:在1Hz的湿度、气压和温度测量模式下为3.6µA,而在不同的操作模式下,气/压/温/湿/气体的功耗在0.1mA至16mA之间变化。 3. 小巧的3.0mm×3.0mm的封装尺寸,高度为1.0mm。 4. 广阔的电源电压范围:...

    中考数学限时训练4份精选.doc

    所以2014万米2等于2.014×10^6米2。 3. 俯视图:第三题涉及几何图形的投影,俯视图是从物体上方看下去的投影,这里需要识别出给定图形的俯视图形状。 4. 二次根式的除法、合并同类项、幂的乘方、完全平方公式:这...

    一年级(下册)思维训练题汇总.doc

    2. **填数游戏**:如003题,需要孩子找到一组能相互配对的数字,使得每个差值都是4。这锻炼了他们的计算能力和寻找模式的能力。 3. **等式求解**:题目004和006涉及到简单的代数概念,孩子们需要理解等式的平衡性,...

    计算机2级C语言考点.pdf

    **2. 十六进制整数** - **定义**: 十六进制整数是以`0x`或`0X`开头的数字序列。 - **例子**: `0x44`表示十进制中的44,而`0X134`表示十进制中的134。 - **错误形式**: `X4f`和`04C`都不是有效的十六进制表示。 ...

    Z014数控铣加工技术赛项规程.doc

    2. **竞赛内容**: - 参赛者需在6小时内完成两个赛件的加工,使用现场提供的设备、软件、工具等。 - 完成赛件加工后,还需进行自我检测,选择合适的量具填写检测结果。 - 职业素养考核包括设备操作规范、工具使用...

    一年级下册思维训练题归纳.doc

    014. 找规律填数的题目要求孩子观察序列并找出其中的规律,然后进行填空。 015. 这些问题考察的是在有序集合中确定位置的能力,通过已知位置推算其他位置的数量。 016. 将加法算式改写为乘法算式,训练孩子对乘法...

    通达信资金博弈成交量副图源码.pdf

    2. **太极成交量 TJCJL**:ZZ:=IF(REF(C,1)&gt;REF(O,1) AND O&gt;REF(C,1)*1.014 AND C*1.02,1,3),这个条件判断语句用来识别某种特定的K线形态,当满足前一天收盘价高于开盘价,开盘价高于前一天收盘价的1.014倍,且收盘...

    PHP开发实战1200例(第1卷).(清华出版.潘凯华.刘中华).part2

    实例131 对数组元素进行随机排序 160 实例132 随机抽取数组中元素 161 实例133 二维数组的输出 162 实例134 获取数组当前的键名和值 162 实例135 检测数组中是否存在某个值 163 实例136 获取数组中的当前单元 164 ...

    C#程序开发范例宝典(第2版).part08

    实例014 OutLook界面 11 实例015 带导航菜单的主界面 12 实例016 图形化的导航界面 14 1.5 特色程序界面 15 实例017 隐藏式窗体 15 实例018 类似Windows XP的程序界面 18 实例019 软件启动界面 19 实例020 以...

    大学物理实验理论考试题及答案.pdf

    2. 物理量的测量和精确度:内容中提到了“0004mm”、“004.0mm2”、“0.014(mm)”等,这可能与测量工具(如游标卡尺、螺旋测微器等)的精度和使用方法,以及如何减小误差和提高测量精度有关。 3. 物理实验的基础...

    五年级数学上册 小数乘以整数课件 沪教版 课件.ppt

    例如,计算2.14乘以5,11.8乘以6,30.014乘以7等,以及判断乘积的小数位数,如0.3乘以30,0.89乘以19等。这些练习有助于学生巩固所学知识,并学会如何在没有计算的情况下预测结果的位数。 此外,还有填空题,比如...

    通达信指标公式源码优化太极量 副图源码.doc

    1. ZZ:如果前一天收盘价高于开盘价,且开盘价高于前一天收盘价的1.014倍,同时当天收盘价低于开盘价的1.02倍,则ZZ设为1;否则,ZZ设为3。这通常表示市场出现一定幅度的波动,可能预示着趋势的变化。 在stickline...

    基于matlab的简易小波去噪.docx

    阈值`0.014`是根据实际情况调整的,用于控制去噪的程度。 完成去噪后,通过`waverec`函数对处理过的系数进行小波重构,得到去噪后的信号`s0`。最后,使用`plot`函数绘制原始信号和去噪后的信号,以便于比较和可视化...

    Python100实例.docx

    2. **控制结构**: - 实例005的三数排序和068的旋转数列,涉及到条件判断和循环结构。 - 实例029的反向输出和032的反向输出II,展示了列表的遍历和反转技巧。 3. **函数与模块**: - 实例034的调用函数和044的...

    VB编程资源大全(源码 其它3)

    o029_snow.zip 一个雪花飘落例子(3KB) 626,o028_zoomsys.zip 类似画图放大镜的东西(2KB) 627,o027_wiz2.zip 制作应用程序向导的例子(类似安装程序的界面)!(5KB) 628,o025_StopWatch.zip “跑表...

Global site tag (gtag.js) - Google Analytics