Нарезка 2D массивов в D - PullRequest
       20

Нарезка 2D массивов в D

6 голосов
/ 21 июня 2011

Если у меня есть двумерный массив в D, я знаю, что могу создать 1D срезы вдоль строк следующим образом:

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

Есть ли простой способ сделать 1D срез вдоль столбцов?Что-то, что делает то, что, как можно подумать,

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

сделает?

Ответы [ 3 ]

4 голосов
/ 21 июня 2011

Вот как может выглядеть пользовательский тип для этого:

// 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);
}
3 голосов
/ 21 июня 2011

Нет, это невозможно.Чтобы это работало, D-кусочки должны иметь шаг.Можно создать пользовательский тип, который работает аналогично срезу (например, std.algorithm.map).

Обратите внимание, что предложенный выше синтаксис будет хорошо компилироваться, но не даст желаемого эффекта.1003 *

2 голосов
/ 22 июня 2011

Если ваш ввод T[][] (то есть динамический массив динамических массивов) и вы хотите получить то же, что и вывод, вы можете выделить новый «внешний» массив и заполнить его срезами внутренних массивов. Это приведет к операции O(n), где обычным срезом будет операция O(1). Кодирование оставлено читателю в качестве упражнения.

...