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