| Thread overview | |||||||
|---|---|---|---|---|---|---|---|
|
July 06, 2015 Coercing ranges to the same type | ||||
|---|---|---|---|---|
| ||||
Say I'm trying to expand an array of file and directory paths (such as ones given as command line args) into a range of file paths I can iterate over. A simplified example might be:
auto getEntries(string[] paths, bool recursive)
{
auto files = paths.filter!(p => p.isFile);
if (recursive) {
auto expandedDirs = paths
.filter!(p => p.isDir)
.map!(p => dirEntries(p, SpanMode.depth, false))
.joiner
.map!(de => de.name); // back to strings
return chain(files, expandedDirs);
}
else {
return files;
}
}
Even though both return statements return a range of strings, this doesn't compile because the result of `chain` is a different type than the result of `filter`. Is there some generic range I could coerce both ranges to in order to have the same return type and make this work? .array is a non-starter since it throws out the ranges' laziness.
| ||||
July 06, 2015 Re: Coercing ranges to the same type | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Matt Kline | On Monday, 6 July 2015 at 19:46:51 UTC, Matt Kline wrote: > Say I'm trying to expand an array of file and directory paths (such as ones given as command line args) into a range of file paths I can iterate over. A simplified example might be: > > auto getEntries(string[] paths, bool recursive) > { > auto files = paths.filter!(p => p.isFile); > > if (recursive) { > auto expandedDirs = paths > .filter!(p => p.isDir) > .map!(p => dirEntries(p, SpanMode.depth, false)) > .joiner > .map!(de => de.name); // back to strings > > return chain(files, expandedDirs); > } > else { > return files; > } > } > > Even though both return statements return a range of strings, this doesn't compile because the result of `chain` is a different type than the result of `filter`. Is there some generic range I could coerce both ranges to in order to have the same return type and make this work? .array is a non-starter since it throws out the ranges' laziness. They aren't actually the same types; one is a `FilterRange!(string[])`; the other a `ChainRange!(string[], MapRange!(...))`. Since they're structs, there's no runtime polymorphism. You can either make `recursive` a template argument (`auto getEntries(bool recursive)(string[] paths)`) with `static if` if you know at compile time when to recurse or not, or use a class wrapper in std.range.interface [1]. [1]: http://dlang.org/phobos/std_range_interfaces.html | |||
July 06, 2015 Re: Coercing ranges to the same type | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Alex Parrill | On Monday, 6 July 2015 at 21:35:53 UTC, Alex Parrill wrote: > They aren't actually the same types I understand the problem - I was just wondering if there was a standard library solution to this or if I would have to roll my own. > use a class wrapper in std.range.interface [1]. > > [1]: http://dlang.org/phobos/std_range_interfaces.html I think I'll go with this one, since the use case would be similar to the '-r' flag in standard Unix utils (copy, mv, etc.) where the user specifies if they want to recurse through provided directories. | |||
July 06, 2015 Re: Coercing ranges to the same type | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Matt Kline | As it turns out, inputRangeObject does an excellent job at this task. The solution then becomes something like:
InputRange!string getEntries(string[] paths, bool recursive)
{
auto files = paths.filter!(p => p.isFile);
if (recursive) {
auto expandedDirs = paths
.filter!(p => p.isDir)
.map!(p => dirEntries(p, SpanMode.depth, false))
.joiner
.map!(de => de.name);
return inputRangeObject(chain(files, expandedDirs));
}
else {
foreach (dir; paths.filter!(p => p.isDir))
stderr.writeln("omitting directory " , dir);
return inputRangeObject(files);
}
}
| |||
July 08, 2015 Re: Coercing ranges to the same type | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Matt Kline | On Monday, 6 July 2015 at 19:46:51 UTC, Matt Kline wrote: > Say I'm trying to expand an array of file and directory paths (such as ones given as command line args) into a range of file paths I can iterate over. A simplified example might be: > > auto getEntries(string[] paths, bool recursive) > { > auto files = paths.filter!(p => p.isFile); > > if (recursive) { > auto expandedDirs = paths > .filter!(p => p.isDir) > .map!(p => dirEntries(p, SpanMode.depth, false)) > .joiner > .map!(de => de.name); // back to strings > > return chain(files, expandedDirs); > } > else { > return files; > } > } > > Even though both return statements return a range of strings, this doesn't compile because the result of `chain` is a different type than the result of `filter`. Is there some generic range I could coerce both ranges to in order to have the same return type and make this work? .array is a non-starter since it throws out the ranges' laziness. I'd say, try to move 'recurse' into a compile time variable, if you need it runtime, move it up a layer: import std.file; import std.range; import std.algorithm; void main(string[] args) { import std.stdio; auto recurse = true; if(recurse) args.getFiles.chain(recurseForFiles(args[])).writeln; else args.getFiles.writeln; } auto getFiles(string[] paths) { return paths.filter!(p => p.isFile); } auto recurseForFiles(string[] paths) { return paths .filter!(p => p.isDir) .map!(p => dirEntries(p, SpanMode.depth, false)) .joiner .filter!(p => p.isFile) .map!(de => de.name); // back to strings } | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply