digitalmars.D.learn - Coercing ranges to the same type
- Matt Kline (24/24) Jul 06 2015 Say I'm trying to expand an array of file and directory paths
- Alex Parrill (10/34) Jul 06 2015 They aren't actually the same types; one is a
- Matt Kline (8/11) Jul 06 2015 I understand the problem - I was just wondering if there was a
- Matt Kline (19/19) Jul 06 2015 As it turns out, inputRangeObject does an excellent job at this
- Jesse Phillips (26/50) Jul 07 2015 I'd say, try to move 'recurse' into a compile time variable, if
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.
Jul 06 2015
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
Jul 06 2015
On Monday, 6 July 2015 at 21:35:53 UTC, Alex Parrill wrote:They aren't actually the same typesI 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.htmlI 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.
Jul 06 2015
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); } }
Jul 06 2015
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 }
Jul 07 2015