contiguous ranges
Vlad Levenfeld via Digitalmars-d
digitalmars-d at puremagic.com
Fri Feb 20 04:23:48 PST 2015
I feel the bigger picture is being missed amid the focus on
blitting.
On Thursday, 19 February 2015 at 11:20:13 UTC, Guillaume Chatelet
wrote:
> From this discussion I understand you mainly want to be able to
> BitBlt ranges
BitBlit is useful, yes, but not the main point. Interfacing with
C APIs and supporting in-place transformations is also important.
On Thursday, 19 February 2015 at 19:32:26 UTC, Andrei
Alexandrescu wrote:
> I don't see a need for contiguous ranges if the only embodiment
> is T[]. -- Andrei
Agreed. I don't think that defining contiguous ranges as
implicitly convertible to T[] is useful - in this case, you could
just write functions to take a T[], and forget the original range
type entirely. For a range concept to be useful for generic
programming, it needs to enable templates to lift algorithms to
the argument type's category. Instead, lowering a range to T[]
loses the capabilities and constraints defined in the original
range and obviates the need for the category in the first place.
On Friday, 20 February 2015 at 01:25:34 UTC, deadalnix wrote:
> Isn't it what a slice is already ?
Yes. A slice is the simplest realization of a contiguous range. I
argue that it is useful to have structures which have slice-like
behavior without actually being slices.
On Friday, 20 February 2015 at 08:20:50 UTC, Ola Fosheim Grøstad
wrote:
> So that you can assess whether "a pointer" (iterator) is in a
> range on or not at O(1)?
Hadn't thought of that, it could be useful.
On Friday, 20 February 2015 at 08:22:38 UTC, John Colvin wrote:
> I don't see anything here that isn't already covered by
> built-in slices, opSlice, opSliceAssign and put.
opSlice.* and put are defined in the data structure, whereas
range concepts inform the algorithm.
//////////////////////////
I claim that the practical benefit of range concepts as type
predicates is:
1) to make overloading logic more succinct and uniform.
2) to concentrate implementation decisions in the appropriate
abstraction layer.
both of which serve to make generic code more robust, expressive
and efficient.
Sometimes you want something like a slice (in that it guarantees
contiguous memory layout) but with additional capabilities and
constraints which you would like to have preserved under certain
transformations. This can be encapsulated in a contiguous range
concept.
Let me illustrate with an example:
struct AudioClip {
float[2][] samples;
uint sampling_rate;
this (string path) {load (path, samples);}
void play () {some_C_audio_lib_call (samples.ptr,
samples.length, sampling_rate);}
}
// we'll start with an audio clip with contiguous layout in
memory
auto ac = AudioClip ("90 minutes of screaming cats.flac");
// now we define two implementations of the same filter
auto ref declaw_filter1 (R)(R r) if (isContiguousRange!R) {
// pattern match to get the size of the sample
static if (is (ElementType!R == float[stride], uint stride))
{}
// i know the gsl functions don't work like this, just bear
with me
gsl_fft (ptr, stride, r.length);
gsl_fgemm (/*pretend this is sensible*/);
gsl_fft_inverse (ptr, stride, r.length);
return r;
}
auto declaw_filter2 (uint n)(float[n][] slice) {
// like declaw_filter1 but without the contiguous stuff
}
// now, we can finish this one of two ways
// the easy way:
ac.declaw_filter1.play; // UFCS all day
// or, if we passed a T[] made from AudioClip or defined
AudioClip to be implicitly convertible to T[]:
AudioClip ac2;
ac2.samples = ac.declaw_filter2;
ac2.sampling_rate = ac.sampling_rate; // this could
desynchronize after the code is refactored, opening us up for bugs
ac2.play;
THE UPSHOT:
Both variants of declaw_filter are generic - they'll operate on
data in an arbitrary number of channels, it could be an audio
file or voltages from a DAQ or whatever.
Variant 1 lifts the algorithm to accommodate the range, while
variant 2 lowers the range to accommodate the algorithm.
The use of variant 2 is more verbose, error-prone, and bubbles
implementation details up to a layer where they don't belong.
Variant 1 uses the concept of a contiguous range to keep the
knowledge of its implementation (specifically, that it calls
functions which expect pointers) encapsulated.
THE DOWNSIDE:
Slicing and in-place transformations are pretty much the only
things that will preserve contiguity. Piping ac through map or
something will take us back to manually propagating the sampling
rate. In general, deciding how and when to preserve what
information under which transformations is tough. Lazily mapping,
say, to increase the volume could meaningfully preserve sampling
rate, but under filtering, zipping or striding it doesn't make
sense.
More information about the Digitalmars-d
mailing list