Небольшая подборка полезных фрагментов кода на D

В этой статье мы покажем ряд полезных фрагментов кода, которые известны практически каждому программисту на D. Здесь не будет каких-то интересных трюков и неожиданных решений, лишь скромная доля маленьких рецептов, которые способны упростить работу и которые иногда забываются.

Полная очистка динамического массива

Иногда требуется полностью опустошить динамический массив и для массива T[] это можно сделать следующим образом:

T[] a;
a.length = 0;

Привести статический массив (массив с фиксированным размером) к динамическому массиву

Для массива T[n] (где n – это количество элементов в массиве) подобное приведение выполняется следующим образом:

T[n] a;
T[] b = a[];

Получить изменяемую копию массива

Для неизменяемого (immutable) массива T[] это можно сделать так:

immutable(T[]) a;
T[] b = a.dup;

Похожим образом выполняется обратный переход, но используется свойство .idup. Вообще, это выполняется не только для массивов, но и для остальных типов данных, у которых определены .dup и .idup, при необходимости эти свойства добавляются вручную созданием либо функций с именами dup/idup, либо соответствующих методов.

Использование индекса в цикле foreach для алгоритмов, принимающих диапазоны

Практически каждому программисту на D знакома вот такая форма для цикла foreach:

foreach (i, e; array)
{
	// operations
}

где i – переменная, в которую попадает автоматически рассчитанный индекс элемента; e – сам элемент некой последовательности; array – некий массив элементов.

Это удобно и очень просто, но если вместо массива используется диапазон, особенно если его предварительно прогнали через целую цепочку алгоритмов, то работать это не будет. Однако, если импортировать enumerate из std.range и немного дополнить цикл foreach, то такая конструкция будет работать:

import std.range : enumerate;
foreach (i, e; range.enumerate)
{
	// operations
}

где range – это диапазон для обработки.

Вот любопытный пример этой “идиомы” для получения изображения фурье-образа тестового сигнала, использующий нашу библиотеку ppmformats:

import std.stdio;

auto fastAbs(float x) @system
{
    uint p = *(cast(uint*) &x);
    p &=0x7fffffff;
    return *(cast(float*) &p);
}

auto renormalize(Range)(Range r)
{
    import std.algorithm;

    auto maximalAbsoluteValue = r
                                .map!(a => fastAbs(a))
                                .fold!max;
    return r
            .map!(a => a / maximalAbsoluteValue);
}

void main()
{
    import std.algorithm;
    import std.complex;
    import std.math;
    import std.numeric;
    import std.random;
    import std.range;

    import ppmformats;

    auto rng = Random(unpredictableSeed);

    auto signalWithNoise(float x)
    {
        return (3.3 * sin(x)) + uniform(-0.3, 0.3, rng);
    }

    auto signal = iota(0, 2_048)
                        .map!signalWithNoise;

    auto normalizedFFT(Range)(Range r)
    {
        return
                r
                  .fft
                  .map!(a => 20.0f * log10(abs(a ^^ 2)))
                  .renormalize;
    }

    auto pd = signal
                    .slide(1_024)
                    .map!(a => normalizedFFT(a));

    auto img = new P6Image(1024, 1024);

    foreach (i, e; pd.enumerate)
    {
        foreach (j, s; e.enumerate)
        {
            img[i, j] = new RGBColor(
                cast(int) (0.8 * 255.0 * abs(s)),
                0,
                255 - cast(int) (255 * abs(s))
            );
        }
    }
    img.save(`fft.ppm`);        
}

Результат предлагаем оценить самостоятельно.

Добавить комментарий