【问题标题】:D dynamic array initialization, stride and the index operationD 动态数组初始化、步幅和索引操作
【发布时间】:2011-12-02 22:55:32
【问题描述】:

抱歉,这变成了关于数组的三重问题

我认为(动态)数组在 D 中确实很强大,但以下问题一直困扰着我:

在 C++ 中,我可以轻松地分配具有指定值的数组,但在 D 中我还没有找到这样做的方法。下面的肯定没问题:

int[] a = new int[N];
a[] = a0;

但它看起来效率低下,因为第一行将使用0 进行初始化,而第二行将使用a0 进行初始化。可以在D中做类似下面的事情吗?

int[] a = new int(a0)[N]; // illegal

在 std.range 中使用 stride 时我遇到的另一个效率问题:

import std.stdio;
import std.range;

struct S
{
    int x;

    this(this)
    {
        writeln("copy ", x);
    }
}

void f(S[] s)
{
}

int main()
{
    S[] s = new S[10];
    foreach (i, ref v; s)
    {
        v.x = i;
    }

    f(stride(s, 3)); // error
    return 0;
}

我当然天真地认为我可以简单地使用 stride 创建一个新数组而不复制它的元素?在 D 中没有办法这样做,对吧?


所以我去模拟,好像数组是步幅会返回的,并将f 实现为:

f(s, 3);

void f(S[] s, uint stride)
{
    ref S get(uint i)
    {
        assert (i * stride < s.length);
        return s[i * stride];
    }

    for (uint x ... )
    {
        get(x) = ...;
    }
}

有没有办法使用索引运算符get[x] 来代替编写 get(x)?这样我可以静态混合/包含跨步 get 函数并保持函数的其余部分相似。我会对所采用的方法感兴趣,因为不允许本地结构访问函数范围变量(为什么不呢?)。

【问题讨论】:

    标签: arrays d dynamic stride


    【解决方案1】:

    但它看起来效率低下,因为第一行将初始化为 0,而第二行将初始化为 a0。可以在D中做类似下面的事情吗?

    使用std.array.uninitializedArray

    S[] s = uninitializedArray!(S[])(N);
    s[] = a0; 
    

    我当然天真地认为我可以简单地使用 stride 创建一个新数组而不复制它的元素?在 D 中没有办法这样做,对吧?

    您的函数f 有一个S[] 作为参数,这与stride 返回的不同。解决这个问题的 D 方法是让您的 f 函数通过将其设为模板来接受任何范围:

    void f(Range)(Range s)
    {
        foreach (item; s)
            // use item
    }
    
    S[] s = new S[10];
    f(s); // works
    f(stride(s, 3)); // works too
    

    您也可以复制数组:

    f(array(stride(s, 3)));
    

    但如果数组很大,您可能希望避免复制整个数组。


    有没有一种方法可以使用索引运算符 get[x] 来代替 get(x)?这样我可以静态地混合/包含跨步 get 函数并保持函数的其余部分相似。我会对所采用的方法感兴趣,因为不允许本地结构访问函数范围变量(为什么不呢?)。

    您可以在自己的结构中重载索引运算符。

    struct StrideArray
    {
        this(S[] s, uint stride) { m_array = s; m_stride = stride; }
    
        S opIndex(size_t i) { return s[i * m_stride]; }
        void opIndexAssign(size_t i, S value) { s[i * m_stride] = value; }
    
        private S[] m_array;
        private uint m_stride;
    }
    

    这是(某种)实际stride 函数的工作方式。我建议阅读Ranges

    【讨论】:

    • 感谢您的阅读。尽管在某些结构中性能略有下降(也许我滥用了它们..),但代码更加简洁和通用。为什么opIndex和opIndexAssign的区别,opIndex不能简单的返回一个ref?
    • 性能问题已解决。在将 C lib 中的数组从指针转换为 Range 之后,代码变得更加简洁。没有发现速度差异。
    • void f(Range)(...) 应该受到限制。 isInputRange ?
    • @Alexander:取决于他想做什么。如果他想写入范围,那么输入范围就不好了。
    • 我现在用 isForwardRange 约束它。我需要独立读写这些值。
    【解决方案2】:

    您可以使用 .dup 复制(创建副本)数组(这也适用于切片),或者您可以使用数组初始值设定项设置元素

    int[] a=a0.dup;
    int[] b=[e1,e2,e3];
    

    你可以使 f 泛型(stride() 返回一个你可以迭代的结构,而不是一个数组)

    void f(Z)(Z s)if(isInputRange!Z){
        foreach(elem;s){
             //...
        }
    }
    

    请记住,数组本质上是具有指向某个内存块的指针字段和大小字段的结构

    【讨论】:

    • 如果a0是一个值,则dup成员不存在。并且做:int[] b=[a0, a0, a0, a0 ... (N times)]; 是艰苦的工作。因此,除了元素的默认构造函数外,没有其他方法可以分配数组(例如将 int 的值设置为 0,对于结构相同的东西)。
    猜你喜欢
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 2011-12-31
    • 2019-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多