用 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