digitalmars.D.learn - std.range pipelike interface, inverting OutputStreams?
I was trying to use std.regex, and it takes an output stream to pump the result to, which is great, but I wanted to perform TWO replacements, one with the output of the previous, with data that might be a tricky size to cache redundantly. So, ideally when you do something like regexp replacing, you'd have a function like: InputRange replaceThingy(InputRange source, someregex, somereplacer); It doesn't do that, unfortunately, so I can't easily chain replacements. But couldn't I do a thing like... InputRange pipe(void delegate(ref OutputRange)) { ? } and then just... auto result = pipe((sink) => replaceAllInto(sink, data, firstmatch, firstreplacer)); auto result2 = pipe((sink) => replaceAllInto(sink, result, secondmatch, secondreplacer)); copy(result2,stdout); ... or something like that? Has that been done before? Can it be done? I'm pretty sure any algorithm that takes an output stream can be transformed into an algorithm that provides an input stream, but I'm not sure if I'd be reinventing the wheel here, or if my assumptions are seriously off. Hoping to avoid weird stuff like context switches... eh.
Aug 12 2016
Here's how to do it using context switches. There ought to be a way to manually pass specific state around to keep that from happening, but probably not since there's no interface to pause something writing to an OutputRange. auto pipe(T, alias oh)() { import std.concurrency: Generator, yield; return new Generator!T({ struct OutputRange { void put(T item) { yield(item); } } oh(OutputRange()); }); }
Aug 12 2016
On Saturday, 13 August 2016 at 02:15:55 UTC, cy wrote:I was trying to use std.regex, and it takes an output stream to pump the result to, which is great, but I wanted to perform TWO replacements, one with the output of the previous, with data that might be a tricky size to cache redundantly. So, ideally when you do something like regexp replacing, you'd have a function like: InputRange replaceThingy(InputRange source, someregex, somereplacer); It doesn't do that, unfortunately, so I can't easily chain replacements. But couldn't I do a thing like... InputRange pipe(void delegate(ref OutputRange)) { ? } and then just... auto result = pipe((sink) => replaceAllInto(sink, data, firstmatch, firstreplacer)); auto result2 = pipe((sink) => replaceAllInto(sink, result, secondmatch, secondreplacer)); copy(result2,stdout); ... or something like that? Has that been done before? Can it be done? I'm pretty sure any algorithm that takes an output stream can be transformed into an algorithm that provides an input stream, but I'm not sure if I'd be reinventing the wheel here, or if my assumptions are seriously off. Hoping to avoid weird stuff like context switches... eh.Looks like you can't just because std.regex replace* functions accept only someString as input. But you can write something like this (sorry for dirty code): import std.regex; import std.stdio; class ReplacePipe { private { Regex!char _re; string _format; string _input; ReplacePipe _next; } this(string input, string r, string f) { _re = regex(r); _format = f; _input = input; } this(ReplacePipe next, string r, string f) { _re = regex(r); _format = f; _next = next; } string go(string i) { return replaceAll(i, _re, _format); } string go() { if ( _next ) { auto s = _next.go(); return replaceAll(s, _re, _format);; } return go(_input); } } ReplacePipe replacePipe(string data, string r, string c) { return new ReplacePipe(data, r, c); } ReplacePipe replacePipe(ReplacePipe output, string r, string c) { return new ReplacePipe(output, r, c); } void main() { string input = "1 2, 3 4"; auto a = input.replacePipe(r" ", "+").replacePipe(r"\+", ".").go(); assert(a=="1.2,.3.4"); }
Aug 13 2016