用 D 语言添加两个数组背后的机制
Mechanics behind adding two arrays in D language
我对下面代码背后的机制有点好奇:
int[3] a1 = [ 1 , 2 , 3 ];
int[3] a2 = [ 1 , 2 , 3 ];
int[3] result = a1[] + a2[];
foreach (i; result)
writeln(i);
结果是 2,4,6 。在 C++ 中,我们必须重载“+”运算符,以便它采用两个向量来实现此目的或使用 std::transform。我检查了一点 array(std_array.html) 的实现文档。我找不到 '+' 的任何重载,我认为 D 以某种方式通过检查数据整数类型或其他方式来管理它,但我只是在猜测。
有人可以解释一下这实际上是如何工作的吗?
它是语言本身的一部分:
http://dlang.org/arrays.html#array-operations
只要给出结果,实现就可以做不同的事情,这为自动优化提供了足够的灵活性。目前,查看反汇编,它编译为函数调用,类似于运算符重载,只是自动完成。
我在 D 中的实现可能是错误的,但我认为这将非常接近。
D中的数组由一个长度和一个指针组成。这种事情背后的机制非常简单。您基本上是为默认大小分配内存,一旦数组充满元素并附加另一个元素,您只需重新分配内存并添加元素。
这是我做的一些快速代码。它只经过最低限度的测试。
C 库:
import std.c.stdlib : malloc, realloc, free;
import std.c.string : memcpy;
来源:
/**
* A dynamic array.
* T would be the type of the array
*/
struct DynamicArray(T) {
T* array; // A pointer to the elements of the array
size_t nextindex; // The next index
size_t size; // The size of the array
/**
* Allocates a new dynamic array with an initialization size.
*/
this(size_t size = 0) {
array = cast(T*)malloc(size * T.sizeof); // Allocates memory of the array for the default array size
nextindex = 0; // The next index is 0 (Arrays are always 0 indexed)
this.size = size; // The size of the array
}
/**
* Appends an element to the array and reallocates memory if necessary.
*/
void appendElement(T e) {
if (nextindex == size) { // If the next index is the size of the array
size++; // Add one to the size (Since we're appending one element)
array = cast(T*)realloc(array, size * T.sizeof); // Reallocate memory to fit the element
}
array[nextindex++] = e; // Adds the element to the index
}
/**
* Appends another array to the array and reallocates memory if necessary.
*/
void appendArray(DynamicArray!T a) {
// This could be done using realloc too and memcpy
foreach (i; 0 .. a.size) {
appendElement(a.array[i]);
}
}
/**
* Gets an element by index.
*/
T getByIndex(size_t index) {
return array[index];
}
/**
* Gets a string of the array elements.
*/
string toString() {
import std.string : format;
import std.array : join;
import std.conv : to;
string[] elements;
foreach (i; 0 .. size)
elements ~= to!string(getByIndex(i));
return format("[%s]", join(elements, ", "));
}
~this() {
free(array);
}
}
用法:
auto array1 = DynamicArray!int(5); // Initialize it with 5
foreach (int i; 0 .. 10) // Loops from 0 to 10, although the actual size of the array is 5
array1.appendElement(i); // Appends i to the array and expands it as necessary
writeln(array1.toString); // Prints out the array
auto array2 = DynamicArray!int(10); // Initialize it with 10
foreach (int i; 0 .. 10) // Loops from 0 to 10
array2.appendElement(i); // Appends i to the array, but does no reallocations, because the size is already 10
writeln(array2.toString); // Prints out the array
array1.appendArray(array2); // Appends array2 to array1
writeln(array1.toString); // Prints out array1
输出:
当然不应该使用这样的东西,我几乎可以肯定 D 中动态数组的实现有点复杂和不同,但这应该让您了解它是如何完成的。
编辑
这是一个与您发布的内容相同的示例。
auto a1 = DynamicArray!int(3); // int[3] a1
// = [
a1.appendElement(1); // 1,
a1.appendElement(2); // 2,
a1.appendElement(3); // 3
// ];
auto a2 = DynamicArray!int(3); // int[3] a2
// = [
a2.appendElement(1); // 1,
a2.appendElement(2); // 2,
a2.appendElement(3); // 3
// ];
auto result = DynamicArray!int(3); // int[3] result
// result =
result.appendArray(a1); // a1
// +
result.appendArray(a2); // a2;
编辑
刚刚查看了 DMD 源代码,我已经非常接近实现了。数组实现有一个调用 malloc/realloc 的保留函数,插入函数也调用保留函数。
https://github.com/D-Programming-Language/dmd/blob/master/src/root/array.h#L76
和
https://github.com/D-Programming-Language/dmd/blob/master/src/root/array.h#L198
我对下面代码背后的机制有点好奇:
int[3] a1 = [ 1 , 2 , 3 ];
int[3] a2 = [ 1 , 2 , 3 ];
int[3] result = a1[] + a2[];
foreach (i; result)
writeln(i);
结果是 2,4,6 。在 C++ 中,我们必须重载“+”运算符,以便它采用两个向量来实现此目的或使用 std::transform。我检查了一点 array(std_array.html) 的实现文档。我找不到 '+' 的任何重载,我认为 D 以某种方式通过检查数据整数类型或其他方式来管理它,但我只是在猜测。
有人可以解释一下这实际上是如何工作的吗?
它是语言本身的一部分:
http://dlang.org/arrays.html#array-operations
只要给出结果,实现就可以做不同的事情,这为自动优化提供了足够的灵活性。目前,查看反汇编,它编译为函数调用,类似于运算符重载,只是自动完成。
我在 D 中的实现可能是错误的,但我认为这将非常接近。
D中的数组由一个长度和一个指针组成。这种事情背后的机制非常简单。您基本上是为默认大小分配内存,一旦数组充满元素并附加另一个元素,您只需重新分配内存并添加元素。
这是我做的一些快速代码。它只经过最低限度的测试。
C 库:
import std.c.stdlib : malloc, realloc, free;
import std.c.string : memcpy;
来源:
/**
* A dynamic array.
* T would be the type of the array
*/
struct DynamicArray(T) {
T* array; // A pointer to the elements of the array
size_t nextindex; // The next index
size_t size; // The size of the array
/**
* Allocates a new dynamic array with an initialization size.
*/
this(size_t size = 0) {
array = cast(T*)malloc(size * T.sizeof); // Allocates memory of the array for the default array size
nextindex = 0; // The next index is 0 (Arrays are always 0 indexed)
this.size = size; // The size of the array
}
/**
* Appends an element to the array and reallocates memory if necessary.
*/
void appendElement(T e) {
if (nextindex == size) { // If the next index is the size of the array
size++; // Add one to the size (Since we're appending one element)
array = cast(T*)realloc(array, size * T.sizeof); // Reallocate memory to fit the element
}
array[nextindex++] = e; // Adds the element to the index
}
/**
* Appends another array to the array and reallocates memory if necessary.
*/
void appendArray(DynamicArray!T a) {
// This could be done using realloc too and memcpy
foreach (i; 0 .. a.size) {
appendElement(a.array[i]);
}
}
/**
* Gets an element by index.
*/
T getByIndex(size_t index) {
return array[index];
}
/**
* Gets a string of the array elements.
*/
string toString() {
import std.string : format;
import std.array : join;
import std.conv : to;
string[] elements;
foreach (i; 0 .. size)
elements ~= to!string(getByIndex(i));
return format("[%s]", join(elements, ", "));
}
~this() {
free(array);
}
}
用法:
auto array1 = DynamicArray!int(5); // Initialize it with 5
foreach (int i; 0 .. 10) // Loops from 0 to 10, although the actual size of the array is 5
array1.appendElement(i); // Appends i to the array and expands it as necessary
writeln(array1.toString); // Prints out the array
auto array2 = DynamicArray!int(10); // Initialize it with 10
foreach (int i; 0 .. 10) // Loops from 0 to 10
array2.appendElement(i); // Appends i to the array, but does no reallocations, because the size is already 10
writeln(array2.toString); // Prints out the array
array1.appendArray(array2); // Appends array2 to array1
writeln(array1.toString); // Prints out array1
输出:
当然不应该使用这样的东西,我几乎可以肯定 D 中动态数组的实现有点复杂和不同,但这应该让您了解它是如何完成的。
编辑
这是一个与您发布的内容相同的示例。
auto a1 = DynamicArray!int(3); // int[3] a1
// = [
a1.appendElement(1); // 1,
a1.appendElement(2); // 2,
a1.appendElement(3); // 3
// ];
auto a2 = DynamicArray!int(3); // int[3] a2
// = [
a2.appendElement(1); // 1,
a2.appendElement(2); // 2,
a2.appendElement(3); // 3
// ];
auto result = DynamicArray!int(3); // int[3] result
// result =
result.appendArray(a1); // a1
// +
result.appendArray(a2); // a2;
编辑
刚刚查看了 DMD 源代码,我已经非常接近实现了。数组实现有一个调用 malloc/realloc 的保留函数,插入函数也调用保留函数。
https://github.com/D-Programming-Language/dmd/blob/master/src/root/array.h#L76
和
https://github.com/D-Programming-Language/dmd/blob/master/src/root/array.h#L198