iota function (the analog in Python is called xrange ) and returns an array of 5x5x40. import std.range : iota; import std.experimental.ndslice; void main() { auto slice = sliced(iota(1000), 5, 5, 40); } auto keyword.sliced function returns a multi-dimensional slice. At the input, it can take as simple arrays as well as ranges . As a result, we have a 5x5x40 cube consisting of numbers from 0 to 999.front , which returns the first element of the range, popFront , which goes to the next element and empty returns a boolean value indicating that the looped sequence is empty. Ranges allow you to perform lazy brute-force access to data as they are needed. The concept of Ranges is covered in more detail here .iota also allows you to generate lazy ranges, and sliced in lazy mode also receives data from iota and processes them as it is received.stdin , filter only unique strings, sort them, and then return to stdout . import std.stdio; import std.array; import std.algorithm; void main() { stdin // get stdin as a range .byLine(KeepTerminator.yes) .uniq // stdin is immutable, so we need a copy .map!(a => a.idup) .array .sort // stdout.lockingTextWriter() is an output range, meaning values can be // inserted into to it, which in this case will be sent to stdout .copy(stdout.lockingTextWriter()); } slice has three dimensions, this is a range that returns ranges of ranges (ranges of ranges). This is clearly seen in the following example: import std.range : iota; import std.stdio : writeln; import std.experimental.ndslice; void main() { auto slice = sliced(iota(1000), 5, 5, 40); foreach (item; slice) { writeln(item); } } [[0, 1, ... 38, 39], [40, 41, ... 78, 79], [80, 81, ... 118, 119], [120, 121, ... 158, 159], [160, 161, ... 198, 199]] ... [[800, 801, ... 838, 839], [840, 841, ... 878, 879], [880, 881, ... 918, 919], [920, 921, ... 958, 959], [960, 961, ... 998, 999]] foreach works much like a for in Python. However, in D you can use it both in C style and in Python cycle style, but without the hassle of enumerate or xrange . import std.range : iota; import std.experimental.ndslice; void main() { auto slice = 1000.iota.sliced(5, 5, 40); } a.func(b) func(a, b) dub init and in the file \source\app.d we write: import std.experimental.ndslice; void main() { } std.experimental.ndslice; located in the std.experimental section. This does not mean that it is raw. This means that he needs to insist. dub a = numpy.arange(1000).reshape((5, 5, 40)) b = a[2:-1, 1, 10:20] auto a = 1000.iota.sliced(5, 5, 40); auto b = a[2 .. $, 1, 10 .. 20]; import numpy data = numpy.arange(100000).reshape((100, 1000)) means = numpy.mean(data, axis=0) import std.range; import std.algorithm.iteration; import std.experimental.ndslice; import std.array : array; void main() { auto means = 100_000.iota .sliced(100, 1000) .transposed .map!(r => sum(r) / r.length) .array; } array method. However, in a real application, the result will not be calculated until it is used in another part of the program.map! function map! has an exclamation mark at the end. This means that this is a template function. It allows at the compilation stage to generate code based on the expression specified in its body. Here's a good article on how the patterns themselves in D work .map! the code will work with any input data that is a range. While the Python code will only work with special arrays from Numpy.iota dynamically creates data that the sliced function sliced . And accordingly, we don’t touch the memory until its last relocation. Also D returns an array with a long data type while Numpy is from double . As a result, I rewrote the code and brought the value of the array to 1,000,000 instead of 10,000. Here’s what happened: import std.range : iota; import std.array : array; import std.algorithm; import std.datetime; import std.conv : to; import std.stdio; import std.experimental.ndslice; enum test_count = 10_000; double[] means; int[] data; void f0() { means = data .sliced(100, 10000) .transposed .map!(r => sum(r, 0L) / cast(double) r.length) .array; } void main() { data = 1_000_000.iota.array; auto r = benchmark!(f0)(test_count); auto f0Result = to!Duration(r[0] / test_count); f0Result.writeln; } %timeit function in D std.datetime.benchmark to measure speed. Compiled everything using LDC v0.17 with the following keys: ldmd2 -release -inline -boundscheck=off -O . Or if you are using a dub, then the equivalent of these keys will be the dub --build=release-nobounds --compiler=ldmd2 options dub --build=release-nobounds --compiler=ldmd2 . Python: 145 µs LDC: 5 µs D is 29x faster Python: 1.43 msec LDC: 628 μs D is 2.27x faster np.asarray which will copy it into a new variable. A quick search on github showed that thousands of projects use this crutch. While data could simply be transferred from one function to another without these blank copies. import numpy as np a = [[0.2,0.5,0.3], [0.2,0.5,0.3]] p = np.asarray(a) y = np.asarray([0,1]) sum(a) a.sum() import std.range : iota; import std.algorithm.iteration : sum; import std.experimental.ndslice; void main() { auto slice = 1000.iota.sliced(5, 5, 40); auto result = slice // sum expects an input range of numerical values, so to get one // we call std.experimental.ndslice.byElement to get the unwound // range .byElement .sum; } sum function and it just takes and works, just like any other function from the base library. a = [0] * 1000 a = numpy.zeros((1000)) auto a = repeat(0, 1000).array; auto a = repeat(0, 1000).array.sliced(5, 5, 40); movingWindowByChannel function can also be used in other filters where a sliding window is required. movingWindowByChannel allows movingWindowByChannel to move around the image using a sliding window. Each such window is passed to a filter that calculates pixel values ​​based on the selected zone. /** Params: filter = unary function. Dimension window 2D is the argument. image = image dimensions `(h, w, c)`, where is the number of channels in the image nr = number of rows in the window n = number of columns in the window Returns: image dimensions `(h - nr + 1, w - nc + 1, c)`, where is the number of channels in the image. Dense data layout is guaranteed. */ Slice!(3, C*) movingWindowByChannel(alias filter, C) (Slice!(3, C*) image, size_t nr, size_t nc) { // local imports in D work much like Python's local imports, // meaning if your code never runs this function, these will // never be imported because this function wasn't compiled import std.algorithm.iteration: map; import std.array: array; // 0. 3D // The last dimension represents the color channel. auto wnds = image // 1. 2D composed of 1D // Packs the last dimension. .pack!1 // 2. 2D composed of 2D composed of 1D // Splits image into overlapping windows. .windows(nr, nc) // 3. 5D // Unpacks the windows. .unpack // 4. 5D // Brings the color channel dimension to the third position. .transposed!(0, 1, 4) // 5. 3D Composed of 2D // Packs the last two dimensions. .pack!2; return wnds // 6. Range composed of 2D // Gathers all windows in the range. .byElement // 7. Range composed of pixels // 2D to pixel lazy conversion. .map!filter // 8. `C[]` // The only memory allocation in this function. .array // 9. 3D // Returns slice with corresponding shape. .sliced(wnds.shape); } /** Params: r = input range buf = buffer with length no less than the number of elements in `r` Returns: median value over the range `r` */ T median(Range, T)(Range r, T[] buf) { import std.algorithm.sorting: sort; size_t n; foreach (e; r) { buf[n++] = e; } buf[0 .. n].sort(); immutable m = n >> 1; return n & 1 ? buf[m] : cast(T)((buf[m - 1] + buf[m]) / 2); } void main(string[] args) { import std.conv: to; import std.getopt: getopt, defaultGetoptPrinter; import std.path: stripExtension; // In D, getopt is part of the standard library uint nr, nc, def = 3; auto helpInformation = args.getopt( "nr", "number of rows in window, default value is " ~ def.to!string, &nr, "nc", "number of columns in window, default value is equal to nr", &nc); if (helpInformation.helpWanted) { defaultGetoptPrinter( "Usage: median-filter [<options...>] [<file_names...>]\noptions:", helpInformation.options); return; } if (!nr) nr = def; if (!nc) nc = nr; auto buf = new ubyte[nr * nc]; foreach (name; args[1 .. $]) { import imageformats; // can be found at code.dlang.org IFImage image = read_image(name); auto ret = image.pixels .sliced(cast(size_t)image.h, cast(size_t)image.w, cast(size_t)image.c) .movingWindowByChannel !(window => median(window.byElement, buf)) (nr, nc); write_image( name.stripExtension ~ "_filtered.png", ret.length!1, ret.length!0, (&ret[0, 0, 0])[0 .. ret.elementsCount]); } } Source: https://habr.com/ru/post/277715/
All Articles