Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
January 13, 2016 Output range of ranges to single buffer | ||||
---|---|---|---|---|
| ||||
Is it possible to somehow output a range of ranges to a single string buffer? For example, converting an array of integers to a string with the same representation as the source code.
import std.algorithm;
import std.conv;
import std.string;
void main()
{
auto a = [1, 2, 3, 4, 5];
auto b = '[' ~ a.map!(e => e.to!string).join(", ") ~ ']';
assert(b == "[1, 2, 3, 4, 5]");
}
The above code is straight forward. But I would like to avoid creating the intermediate strings, "e.to!string", and instead put the converted integer and the result of join directly in the same buffer.
A bit more complex example:
struct Foo
{
int a;
string b;
int c;
string toString()
{
auto values = [a.to!string, b, c.to!string].join(", ");
return "Foo(" ~ values ~ ')';
}
}
void main()
{
auto a = [
Foo(1, "foo", 2),
Foo(3, "bar", 4)
];
auto b = '[' ~ a.map!(e => e.to!string).join(", ") ~ ']';
assert(b == "[Foo(1, foo, 2), Foo(3, bar, 4)]");
}
--
/Jacob Carlborg
|
January 13, 2016 Re: Output range of ranges to single buffer | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Wed, Jan 13, 2016 at 10:15:03PM +0100, Jacob Carlborg via Digitalmars-d-learn wrote: > Is it possible to somehow output a range of ranges to a single string buffer? For example, converting an array of integers to a string with the same representation as the source code. > > import std.algorithm; > import std.conv; > import std.string; > > void main() > { > auto a = [1, 2, 3, 4, 5]; > auto b = '[' ~ a.map!(e => e.to!string).join(", ") ~ ']'; > assert(b == "[1, 2, 3, 4, 5]"); > } > > The above code is straight forward. But I would like to avoid creating the intermediate strings, "e.to!string", and instead put the converted integer and the result of join directly in the same buffer. Isn't that just a matter of replacing each of the segments with their range equivalents? Also, std.format.formattedWrite will do writeln-formatting into a buffer (well, any output range, really) -- I'm pretty sure it doesn't allocate, at least for the simplest cases like converting an integer. So you should be able to do something like this: auto data = [ 1, 2, 3, 4, 5 ]; char[] buf = ...; formattedWrite(buf, "[%(%d, %)]", data); T -- Customer support: the art of getting your clients to pay for your own incompetence. |
January 13, 2016 Re: Output range of ranges to single buffer | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Wednesday, 13 January 2016 at 21:15:03 UTC, Jacob Carlborg wrote:
> Is it possible to somehow output a range of ranges to a single string buffer? For example, converting an array of integers to a string with the same representation as the source code.
>
> [...]
std.format can do it. From the site:
import std.stdio;
void main()
{
writefln("My items are %(%s %).", [1,2,3]);
writefln("My items are %(%s, %).", [1,2,3]);
}
|
January 13, 2016 Re: Output range of ranges to single buffer | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On 01/13/2016 01:20 PM, H. S. Teoh via Digitalmars-d-learn wrote:
> std.format.formattedWrite will do
> writeln-formatting into a buffer (well, any output range, really) -- I'm
> pretty sure it doesn't allocate, at least for the simplest cases like
> converting an integer. So you should be able to do something like this:
>
> auto data = [ 1, 2, 3, 4, 5 ];
> char[] buf = ...;
> formattedWrite(buf, "[%(%d, %)]", data);
And buf can be an Appender:
import std.stdio;
import std.format;
import std.array;
void main() {
auto data = [ 1, 2, 3, 4, 5 ];
auto buf = appender!string(); // Or appender!(char[]) if needed
formattedWrite(buf, "[%(%d, %)]", data);
assert(buf.data == "[1, 2, 3, 4, 5]");
}
Ali
|
January 14, 2016 Re: Output range of ranges to single buffer | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On 2016-01-13 22:20, H. S. Teoh via Digitalmars-d-learn wrote: > Isn't that just a matter of replacing each of the segments with their > range equivalents? Also, std.format.formattedWrite will do > writeln-formatting into a buffer (well, any output range, really) -- I'm > pretty sure it doesn't allocate, at least for the simplest cases like > converting an integer. So you should be able to do something like this: > > auto data = [ 1, 2, 3, 4, 5 ]; > char[] buf = ...; > formattedWrite(buf, "[%(%d, %)]", data); Aha, interesting. I didn't know formattedWrite could format an array/range directly like that. The more complex example works by defining "toString" which takes an output range (delegate). But what if I need to format a third party type that I cannot add methods to? UFCS does not seem to work. -- /Jacob Carlborg |
January 14, 2016 Re: Output range of ranges to single buffer | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On 01/13/2016 11:41 PM, Jacob Carlborg wrote: > what if I need to format a third party type that I cannot add > methods to? UFCS does not seem to work. Here is an experiment that wraps the third party type to provide a lazy toString: import std.stdio; import std.format; import std.array; import std.algorithm; import std.range; /* Wraps an element and provides a lazy toString that dispatches the work to a * user-provided 'formatter' function. E is the element type. */ struct Formatted(alias formatter, E) { E element; void toString(void delegate(const(char)[]) sink) const { formatter(sink, element); } } /* Adapts a range by converting the elements to 'Formatted'. R is the range * type. */ auto formatted(alias formatter, R)(R range) { return range.map!(e => Formatted!(formatter, ElementType!R)(e)); } /* A third party test type that does not have a lazy toString member * function. */ struct Foo { double d; string s; } void main() { auto data = [ Foo(1.5, "hello"), Foo(2.5, "world") ]; auto buf = appender!string(); formattedWrite(buf, "%(%s\n%)", data.formatted!( (sink, a) => formattedWrite(sink, "%s and %s", a.d, a.s))); writeln(buf.data); } Prints the objects according to the user's lambda: 1.5 and hello 2.5 and world It would be great if the user could provide just the format and accessed the members: data.formatted!("%s and %s", a.d, a.s); // <-- ERROR But I couldn't get it working because the compiler does not know what 'a' is at that point. It might be acceptable to provide a lambda per member but then it gets to cluttered: data.formatted!("%s and %s", a => a.d, a => a.s); // Might work Ali |
January 14, 2016 Re: Output range of ranges to single buffer | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Thursday, January 14, 2016 08:41:23 Jacob Carlborg via Digitalmars-d-learn wrote:
> On 2016-01-13 22:20, H. S. Teoh via Digitalmars-d-learn wrote:
>
> > Isn't that just a matter of replacing each of the segments with their range equivalents? Also, std.format.formattedWrite will do writeln-formatting into a buffer (well, any output range, really) -- I'm pretty sure it doesn't allocate, at least for the simplest cases like converting an integer. So you should be able to do something like this:
> >
> > auto data = [ 1, 2, 3, 4, 5 ];
> > char[] buf = ...;
> > formattedWrite(buf, "[%(%d, %)]", data);
>
> Aha, interesting. I didn't know formattedWrite could format an array/range directly like that.
>
> The more complex example works by defining "toString" which takes an output range (delegate). But what if I need to format a third party type that I cannot add methods to? UFCS does not seem to work.
You can't do toString via UFCS any more than you can overload any operators via UFCS. If a type's toString does not work how you want, or a type does not provide one, then you'll need to convert objects of that type to a string in a different way.
- Jonathan M Davis
|
January 14, 2016 Re: Output range of ranges to single buffer | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On 2016-01-14 17:59, Ali Çehreli wrote: > Here is an experiment that wraps the third party type to provide a lazy > toString: > > import std.stdio; > import std.format; > import std.array; > import std.algorithm; > import std.range; > > /* Wraps an element and provides a lazy toString that dispatches the > work to a > * user-provided 'formatter' function. E is the element type. */ > struct Formatted(alias formatter, E) { > E element; > > void toString(void delegate(const(char)[]) sink) const { > formatter(sink, element); > } > } Wrap the object, why didn't I think of that :) > /* Adapts a range by converting the elements to 'Formatted'. R is the range > * type. */ > auto formatted(alias formatter, R)(R range) { > return range.map!(e => Formatted!(formatter, ElementType!R)(e)); > } > > /* A third party test type that does not have a lazy toString member > * function. */ > struct Foo { > double d; > string s; > } > > void main() { > auto data = [ Foo(1.5, "hello"), Foo(2.5, "world") ]; > > auto buf = appender!string(); > formattedWrite(buf, "%(%s\n%)", > data.formatted!( > (sink, a) => formattedWrite(sink, "%s and %s", > a.d, a.s))); > > writeln(buf.data); > } > > Prints the objects according to the user's lambda: > > 1.5 and hello > 2.5 and world Thanks. -- /Jacob Carlborg |
Copyright © 1999-2021 by the D Language Foundation