www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - std.range pipelike interface, inverting OutputStreams?

reply cy <dlang verge.info.tm> writes:
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
next sibling parent cy <dlang verge.info.tm> writes:
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
prev sibling parent ikod <geller.garry gmail.com> writes:
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