Thread overview
Need help: Return reference slice
Oct 29, 2014
advibm
Oct 29, 2014
bearophile
Oct 29, 2014
advibm
Oct 29, 2014
Ali Çehreli
Oct 30, 2014
bearophile
Oct 30, 2014
advibm
October 29, 2014
Hello,

I would like to create a D function that returns a slice of a string.
This slice shall be a reference to a part of the string argument.
Is this generally possible in D?

This is the function:
auto ref betweenTwoStrings(T)(inout T src, string start, string end) {
  long a = src.countUntil(start);
  if (a < 0)
    return src; // null
  a += start.length;
  long b = src[a..$].countUntil(end);
  if (b < 0)
    return src; // null
  b += a;
  return src[a..b];
}


I would like to have something like that:

char[] buf; // already filled array
char[] partOfBuf = betweenTwoStrings(buf, "START", "END");
partOfBuf[0] = 'a'; // THIS should also change the 'buf' variable
assert(buf[0] == 'a');

Thanks for your help
October 29, 2014
advibm:

> I would like to have something like that:
>
> char[] buf; // already filled array
> char[] partOfBuf = betweenTwoStrings(buf, "START", "END");
> partOfBuf[0] = 'a'; // THIS should also change the 'buf' variable
> assert(buf[0] == 'a');
>
> Thanks for your help

To do this you don't need to return a ref slice. A slice suffices. But D strings/wstrings/dstrings are made of immutable chars, so you can't modify them. So you need to work with char[] or dchar[] or wchar[].

Bye,
bearophile
October 29, 2014
On Wednesday, 29 October 2014 at 19:54:45 UTC, bearophile wrote:
> advibm:
>
>> I would like to have something like that:
>>
>> char[] buf; // already filled array
>> char[] partOfBuf = betweenTwoStrings(buf, "START", "END");
>> partOfBuf[0] = 'a'; // THIS should also change the 'buf' variable
>> assert(buf[0] == 'a');
>>
>> Thanks for your help
>
> To do this you don't need to return a ref slice. A slice suffices. But D strings/wstrings/dstrings are made of immutable chars, so you can't modify them. So you need to work with char[] or dchar[] or wchar[].
>
> Bye,
> bearophile

Thank you for your fast answer.
I am embarrased but my code already works as expected.
I made a mistake when I wanted to change the partOfBuf variable.
I wrote: partOfBuf = replacedArray;
But I had to write partOfBuf[0..$] = replacedArray.
October 29, 2014
On 10/29/2014 12:50 PM, advibm wrote:
> Hello,
>
> I would like to create a D function that returns a slice of a string.
> This slice shall be a reference to a part of the string argument.
> Is this generally possible in D?
>
> This is the function:
> auto ref betweenTwoStrings(T)(inout T src, string start, string end) {
>    long a = src.countUntil(start);
>    if (a < 0)
>      return src; // null
>    a += start.length;
>    long b = src[a..$].countUntil(end);
>    if (b < 0)
>      return src; // null
>    b += a;
>    return src[a..b];
> }
>
>
> I would like to have something like that:
>
> char[] buf; // already filled array
> char[] partOfBuf = betweenTwoStrings(buf, "START", "END");
> partOfBuf[0] = 'a'; // THIS should also change the 'buf' variable
> assert(buf[0] == 'a');
>
> Thanks for your help

There are pretty useful std.algorithm functions for searching:

import std.algorithm;
import std.traits;
import std.array;

C[] betweenTwoStrings(C)(C[] src, string start, string end)
    if (isSomeChar!C)
{
    if (src.findSkip(start)) {
        auto found = src.findSplitBefore(end);

        // Just for readability:
        auto needleAndAfter = found[1];

        if (!needleAndAfter.empty) {
            auto beforeNeedle = found[0];
            return beforeNeedle;
        }
    }

    return (C[]).init;
}

void main()
{
    char[] buf;
    buf ~= "prefixSTARTactual partENDpostfix";

    char[] partOfBuf = betweenTwoStrings(buf, "START", "END");

    partOfBuf[] = 'a';
    assert(buf == "prefixSTARTaaaaaaaaaaaENDpostfix");
}

Ali

October 30, 2014
On 10/29/14 3:50 PM, advibm wrote:
> Hello,
>
> I would like to create a D function that returns a slice of a string.
> This slice shall be a reference to a part of the string argument.
> Is this generally possible in D?
>
> This is the function:
> auto ref betweenTwoStrings(T)(inout T src, string start, string end) {
>    long a = src.countUntil(start);
>    if (a < 0)
>      return src; // null
>    a += start.length;
>    long b = src[a..$].countUntil(end);
>    if (b < 0)
>      return src; // null
>    b += a;
>    return src[a..b];
> }
>
>
> I would like to have something like that:
>
> char[] buf; // already filled array
> char[] partOfBuf = betweenTwoStrings(buf, "START", "END");
> partOfBuf[0] = 'a'; // THIS should also change the 'buf' variable
> assert(buf[0] == 'a');
>
> Thanks for your help


How I would do it:

inout(T)[] betweenTwoStrings(T, U, V)(inout(T)[] src, const(U)[] start, const(V)[] end) if(allSatisfy!(isSomeChar, T, U, V)) {
  long a = src.countUntil(start);
  if (a < 0)
    return src; // null
  a += start.length;
  long b = src[a..$].countUntil(end);
  if (b < 0)
    return src; // null
  b += a;
  return src[a..b];
}

This should accept any kind of strings, in any constancy for all the parameters, yet guarantee it does not change any data in any of them.

-Steve.
October 30, 2014
Steven Schveighoffer:

>   long a = src.countUntil(start);
>   if (a < 0)
>     return src; // null
>   a += start.length;
>   long b = src[a..$].countUntil(end);

I think there it's better to use "auto" instead of "long".

Bye,
bearophile
October 30, 2014
On 10/30/14 11:06 AM, bearophile wrote:
> Steven Schveighoffer:
>
>>   long a = src.countUntil(start);
>>   if (a < 0)
>>     return src; // null
>>   a += start.length;
>>   long b = src[a..$].countUntil(end);
>
> I think there it's better to use "auto" instead of "long".

Sure, I didn't touch OP's function body, just the signature.

-Steve

October 30, 2014
Thank you very much for your additions.
I am still new to D so I am glad to see solutions from experienced D programmers.