Small iterators/algorithm usage feedback

Brad Roberts braddr at puremagic.com
Sun Apr 26 16:03:49 PDT 2009


dsimcha wrote:
> == Quote from Denis Koroskin (2korden at gmail.com)'s article
>> I have just started using iterators and algorithms a little.
>> Given a full file path, a needed to get folder name where it is located, and a
> name of the that file.
>> Using C and its standard library, I can do it as follows:
>> auto pos = strrpos(fullFilePath, '/'); // strrpos = strpos-reverse
>> string dirName = fullFilePath[0..pos];
>> string fileName = fullFilePath[pos+1..$];
>> It's very simple, I'd use it, but there is no strrpos (nor strpos) in
> Phobos/druntime.
>> So I tried using the following code:
>> auto it = find(retro(fullFilePath), '/'); // returns Iterator!(Retro!(string))
>> The hardest part is complete, I thought, but I was wrong.
>> Now, I realized that I can't do anything with this iterator.
>> I tried get a position, but the following code failed:
>> int pos = rBegin(fullFilePath) - it;
>> Error: incompatible types for ((rBegin(fullFilePath)) - (it)):
> 'Iterator!(Retro!(string))' and 'Iterator!(Retro!(string))'
>> Apparently, there is no opSub() in an Iterator's method set.
>> Okay, let's take a row pointer to a char and calculate the position by hand:
>> int pos = &(*it) - fullFilePath.ptr; // a common C++ practice
>> But it failed, too:
>> Error: it.opStar() is not an lvalue.
>> Oh, well, you can't take an address of a temporary, let's store it first:
>> ref immutable(char) c = *it;
>> immutable(char)* ptr = &c;
>> Error: found 'ref' instead of statement
>> Oops, let's write a workaround:
>> immutable(char)* getAddress(ref immutable(char) c)
>> {
>>     return &c;
>> }
>> Error: function unwrap (ref immutable(char) c) does not match parameter types
> (immutable(char))
>> Oh, it looks like "*it" doesn't return a reference but a copy :(
>> Next, I thought: that's probably right. It's probably error-prone and disallowed
> on purpose. It may be better and closer to range-style to get subranges out of a
> these iterators (begin..it)/(it..end) and finish the task:
>> string dirName = range(begin(fullFilePath), it);
>> string fileName = range(it + 1, end(fullFilePath));
>> But that failed, too:
>> Error: template std.iterator.range(T) does not match any function template
> declaration
>> I tried different other combinations, but all of them failed.
>> I hope I missed something very simple, but that's what I experienced (being a
> professional C++ developer who knows, uses and loves STL). I believe others may
> have similar difficulties, too, so I thought it may be helpful to post my
> experience here. I hope it'll be constructive.
> 
> Yes, std.algorithm is supposed to be very generic, rather than focusing on doing
> one thing well.  This is good when you have some very generic needs, but since
> strings are such a common thing, we have std.string.rfind to handle exactly what
> you're looking for without massive template gymnastics.

I haven't played with any of the recent changes, so I figured this might
be a fun exercise to try myself as well.  Here's what I ended up with:

import std.algorithm;
import std.range;
import std.stdio;

void main()
{
    string inputpath = "/a/b";

    auto parts = splitter(inputpath, "/");
    parts.popFront; // remove unwanted empty element
    writeln("split parts:");
    foreach(a; parts)
        writefln("  a = %s", a);

    auto file = retro(parts).popFront;  // line 19
    writefln("file = %s", file);
    writeln("remainder:");
    foreach(a; parts)
        writefln("  a = %s", a);

    auto path = reduce!(q{a ~= "/" ~ b})("", parts);
    writefln("path = %s", path);
}

The problem is that splitter isn't a bi-dir range, so it fails with:
./path.d(19): Error: template std.range.retro(R) if
(isBidirectionalRange!(R)) does not match any function template declaration
./path.d(19): Error: template std.range.retro(R) if
(isBidirectionalRange!(R)) cannot deduce template function from argument
types !()(Splitter!(immutable(char)[],immutable(char)[]))
./path.d(19): Error: no property 'popFront' for type 'int'

Comment out lines 19 and 20 just to confirm the rest works:
split parts:
  a = a
  a = b
remainder:
  a = a
  a = b
path = /a/b

Looking at the code behind Splitter.. it shouldn't be hard to fix, but I
haven't yet.

Obviously this code does more string (and thus memory) manipulation than
strictly necessary to do this job.  So, for the sake of completeness to
this thread... Denis, you're aware of std.path and its basename and
dirname functions, right?  I assume from the first post in the thread
that this is an exercise of using ranges and algorithms.

Later,
Brad



More information about the Digitalmars-d mailing list