patternMinor
Haskell's intersperse in D
Viewed 0 times
interspersehaskellstackoverflow
Problem
I am learning D. I'm trying to implement Haskell's intersperse in D. I want to use ranges. I feel that the intermediate "interspersed" value is wrong. Additionally, I suspect the concatenation character by character is inefficient. What is a more idiomatic way to implement this function, please?
My initial thought was to try
This however is wrong for at least two reasons:
-
-
This code doesn't typecheck:
string translateToSwedish(immutable string english) pure
{
string interspersed;
foreach(char c; english)
interspersed = interspersed ~ c ~ 'f';
return interspersed;
}
void main()
{
assert(translateToSwedish("Hello") == "Hfeflflfof");
}
My initial thought was to try
std.range.roundRobin with repeat:string translateToSwedish(immutable string english) pure
{
import std.range;
return roundRobin("f".repeat(), english);
}
This however is wrong for at least two reasons:
-
roundRobin is eager and will evaluate the full range even if another range finishes first. Repeat constructs an infinite range, thus my translateToSwedish function is also infinite.-
This code doesn't typecheck:
main.d(6): Error: cannot implicitly convert expression (roundRobin(repeat("f"), english)) of type Result to stringSolution
Its unfortunate that
As @sigod pointed out, the issue with
It is probably the best option, but I'll leave my alternative anyways:
Note the use of the
You'll notice many D functions are written like this. You generally want to keep passing around ranges until it is absolutely necessary to evaluate the result.
With that in mind, lets allow
Now
roundRobin doesn't work here, as it sounds just right for the task. Some functions (e.g. std.range.zip) have a StoppingStrategy that allows you to determine whether to stop at the shortest or longest range, and I'm not sure why roundRobin doesn't support this. As @sigod pointed out, the issue with
roundRobin was just a simple typo (I missed it too!).It is probably the best option, but I'll leave my alternative anyways:
import std.range, std.algorithm;
auto translateToSwedish(immutable string english) pure
{
return english.map!(c => only(c, 'f')).joiner;
}
void main()
{
assert(translateToSwedish("Hello").equal("Hfeflflfof"));
}map replaces each character c with the sequence c'f'.joiner links these pairs of character into a single range.Note the use of the
auto return type. Returning a string would imply eagerly committing the result within translateToSwedish. However, we don't know what the user of translateToSwedish will do with the result; if they are just passing it to another range-manipulating-function, they'd much rather we give them a range.You'll notice many D functions are written like this. You generally want to keep passing around ranges until it is absolutely necessary to evaluate the result.
With that in mind, lets allow
translateToSwedish to operate on a range as well:import std.traits, std.range, std.algorithm;
auto translateToSwedish(R)(R english) pure if (isSomeString!R)
{
return english.map!(c => only(c, 'f')).joiner;
}
void main()
{
assert(translateToSwedish("Hello").equal("Hfeflflfof"));
}Now
translateToSwedish is well equipped to be used in a lazily evaluated function chain:auto result = "Hello"
.translateToSwedish
.translateToGerman
.translateToPigLatin
// and so onCode Snippets
import std.range, std.algorithm;
auto translateToSwedish(immutable string english) pure
{
return english.map!(c => only(c, 'f')).joiner;
}
void main()
{
assert(translateToSwedish("Hello").equal("Hfeflflfof"));
}import std.traits, std.range, std.algorithm;
auto translateToSwedish(R)(R english) pure if (isSomeString!R)
{
return english.map!(c => only(c, 'f')).joiner;
}
void main()
{
assert(translateToSwedish("Hello").equal("Hfeflflfof"));
}auto result = "Hello"
.translateToSwedish
.translateToGerman
.translateToPigLatin
// and so onContext
StackExchange Code Review Q#105929, answer score: 3
Revisions (0)
No revisions yet.