在D语言中切片2D数组

6

如果我有一个二维数组在D中,我知道可以按行创建1D切片,如下所示:

auto one_dim_arr=two_dim_arr[i][0..$]

有没有一种简单的方法可以沿列制作1D切片?做些让人想到的事情。

auto one_dim_arr=two_dim_arr[0..$][j]

would do?

3个回答

4
这是一个用户创建的类型示例:

以下是可能看起来像什么:

// Demo

void main()
{
    int[3][3] arr = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
    ];

    // simple creation
    auto middleColumn = verticalSlice(arr, 1);
    assert(middleColumn[1] == 5);

    // iteratable
    foreach (i, v; middleColumn)
        assert(v == 2+i*3);

    // still a slice - writing will change original array
    middleColumn[1] = 17;
    assert(arr[1][1] == 17);

    // sliceable itself
    auto center = middleColumn[1..2];
    center[0] = 42;
    assert(arr[1][1] == 42);

    // get a normal array with .dup
    int[] copyOfMiddleColumn = middleColumn.dup;
}

// Implementation

struct StepSlice(T)
{
    T* ptr;
    size_t length, step;

    T opIndex(size_t index)
    in { assert(index<length); }
    body { return ptr[step*index]; }

    void opIndexAssign(T value, size_t index)
    in { assert(index<length); }
    body { ptr[step*index] = value; }

    StepSlice!T opSlice(size_t start, size_t end)
    in { assert(start<=end && end<=length); }
    body { return StepSlice!T(ptr+start*step, end-start, step); }

    int opApply(int delegate(ref T) dg)
    {
        int result = 0;

        for (size_t i=0; i<length; i++)
        {
            result = dg(ptr[i*step]);
            if (result)
                break;
        }
        return result;
    }

    int opApply(int delegate(ref size_t, ref T) dg)
    {
        int result = 0;

        for (size_t i=0; i<length; i++)
        {
            result = dg(i, ptr[i*step]);
            if (result)
                break;
        }
        return result;
    }

    T[] dup()
    {
        T[] result = new T[length];
        for (size_t i=0; i<length; i++)
            result[i] = ptr[i*step];
        return result;
    }
}

StepSlice!T verticalSlice(T, size_t W)(T[W][] arr, size_t column)
{
    return StepSlice!T(arr[0].ptr+column, arr.length, W);
}

我认为这里缺少了范围原语,但作为出发点仍然很不错。
使用std.range.stride
import std.range;

// Demo

void main()
{
    int[3][3] arr = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
    ];

    // simple creation
    auto middleColumn = verticalSlice(arr, 1);
    assert(middleColumn[1] == 5);

    // iteratable
    uint i;
    foreach (v; middleColumn)
        assert(v == 2+(i++)*3);

    // still a slice - writing will change original array
    middleColumn[1] = 17;
    assert(arr[1][1] == 17);

    // sliceable itself
    auto center = middleColumn[1..2];
    center[0] = 42;
    assert(arr[1][1] == 42);

    // get a normal array with array()
    int[] copyOfMiddleColumn = array(middleColumn);
}

// Implementation

auto verticalSlice(T, size_t W)(T[W][] arr, size_t column)
{
    T* start = arr[0].ptr+column;
    return stride(start[0..W*arr.length], W);
}

2
@Haunter:这里使用“步幅(stride)”是否有帮助呢? - user541686
1
是的!我正在寻找这样的东西。 - Vladimir Panteleev
1
哈哈,这是我第一次看到一个被接受的答案被转移到原来的人身上... - user541686

3
不,这是不可能的。为了使其工作,D切片需要有一个步骤。可以创建一个类似于切片的自定义类型(例如std.algorithm.map)。
请注意,您上面建议的语法将编译成功,但不会产生您想要的效果。

1
“D切片需要具有步长”是什么意思?我不太理解。 - Andrew Spott
2
如果您知道二维数组在内存中的表示方式以及切片的工作原理,那么您就会知道程序需要知道每行中相同列之间元素的距离。这就是我所说的“步长”。当前的D切片具有隐含的步长为1。 - Vladimir Panteleev
1
据我所知,Python允许使用步长对字符串和数组进行切片,但它会复制数据而不是创建类似D的切片。在C++中也可以实现自定义类型。 - Vladimir Panteleev
我知道它是用Fortran 90编写的,这就是我认为D可能具有此功能的原因。我正在考虑使用D来完成一些非常类似Fortran的事情。 - Dan
啊,有趣,我猜到Fortran可能有这个功能(但不确定)。太棒了! - user541686
显示剩余2条评论

2

如果您的输入是一个T[][](即动态数组的动态数组),并且您想要相同的输出,您可以分配一个新的“外部”数组,并使用内部数组的切片填充它。这将导致O(n)操作,而正常的切片则为O(1)操作。编码留给读者作为练习。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接