I want to sort lines read from stdin by line length, after trimming white spaces off. The code works as expected.
import std.algorithm, std.array, std.stdio, std.string;
void main()
{
stdin
.byLineCopy
.map!(strip)
.array
.sort!((a, b) => a.length < b.length)
.each!writeln;
}
However, if I swap map line with array line,
void main()
{
stdin
.byLineCopy
.array
.map!(strip)
.sort!((a, b) => a.length < b.length)
.each!writeln;
}
The program fails to compile, with the following error,
sort_lines.d(9): Error: template std.algorithm.sorting.sort cannot deduce function from argument types !((a, b) => a.length < b.length)(MapResult!(strip, string[])), candidates are:
/usr/include/dmd/phobos/std/algorithm/sorting.d(1852): std.algorithm.sorting.sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range)(Range r) if ((ss == SwapStrategy.unstable && (hasSwappableElements!Range || hasAssignableElements!Range) || ss != SwapStrategy.unstable && hasAssignableElements!Range) && isRandomAccessRange!Range && hasSlicing!Range && hasLength!Range)
Failed: ["/usr/bin/dmd", "-v", "-o-", "sort_lines.d", "-I."]
I cannot understand why. Doesn't map function applies to each element in a range and return a new range? According to documentation, sort requires its range argument to be random-accessible. Is it the reason? If so, what is the type of the range returned by map?
map returns a range that does not have assignable or swappable elements - another requirement that sort specifies. The solution is, as in your first example, to iterate to an array first.
The elements are not assignable because map does computation and does not return a value by ref. Thus, even if you did grab the address of the returned element, and overwrote that with the new value for that position, it wouldn't change the value in the actual backing array.
The problem is actually slightly worse than this. What [1,2,3].map!(a => a * 2) does is it takes an element from [1,2,3] and multiplies it by 2. If I did something like [1,2,3].map!(a => a * 2)[0] = 4, that would have to invert the function a => a * 2 to figure out the value to put into [1,2,3], and turn it into [2,2,3]. In the above case that's relatively easy, but what if I tried to assign 5 instead of 4? What if the mapping function is a one-way hash? As we can see, the only real solution is a store with the mapped values - i.e. [1,2,3].map.array.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With