December 11, 2021

On Friday, 10 December 2021 at 23:53:47 UTC, Ola Fosheim Grøstad wrote:

>
char[] dontdothis(string s, int i=0, int skip=0){
    if (s.length == i) return new char[](i - skip);
    if (s[i] == ';') return dontdothis(s, i+1, skip+1);
    auto r = dontdothis(s, i+1, skip);
    r[i-skip] = s[i];
    return r;
}

That is about 500% not what I meant. At all. Original code in question:

  • duplicates string unconditionally as mutable storage
  • uses said mutable storage to gather all non-semicolons
  • duplicates said mutable storage (again) as immutable

I suggested to make the second duplicate conditional, based on amount of space freed by skipping semicolons.

What you're showing is... indeed, don't do this, but I fail to see what that has to do with my suggestion, or the original code.

>

Scanning short strings twice is not all that expensive as they will stay in the CPU cache > when you run over them a second time.

>
import std.stdio;

@safe:
string stripsemicolons(string s) @trusted {
    int i,n;
    foreach(c; s) n += c != ';'; // premature optimization
    auto r = new char[](n);
    foreach(c; s) if (c != ';') r[i++] = c;
    return cast(string)r;
}

Again, that is a different algorithm than what I was responding to. But sure, short strings - might as well. So long as you do track the distinction somewhere up in the code and don't simply call this on all strings.

December 11, 2021

On Saturday, 11 December 2021 at 09:26:06 UTC, Stanislav Blinov wrote:

>

What you're showing is... indeed, don't do this, but I fail to see what that has to do with my suggestion, or the original code.

You worry too much, just have fun with differing ways of expressing the same thing.

(Recursion can be completely fine if the compiler supports it well. Tail recursion that is, not my example.)

>

Again, that is a different algorithm than what I was responding to.

Slightly different, but same idea. Isn't the point of this thread to present N different ways of doing the same thing? :-)

December 11, 2021

On Saturday, 11 December 2021 at 08:46:32 UTC, forkit wrote:

>

On Saturday, 11 December 2021 at 08:05:01 UTC, Ola Fosheim Grøstad wrote:

>

Using libraries can trigger hidden allocations.

ok. fine. no unnecessary, hidden allocations then.

// ------------------

module test;

import core.stdc.stdio : putchar;

nothrow @nogc void main()
{
string str = "abc;def;ab";

ulong len = str.length;

for (ulong i = 0; i < len; i++)
{
    if (cast(int) str[i] != ';')
        putchar(cast(int) str[i]);
}

}

// ------------------

putchar(…) is too slow!


@safe:

extern (C) long write(long, const void *, long);


void donttrythisathome(string s, char stripchar) @trusted {
	import core.stdc.stdlib;
    char* begin = cast(char*)alloca(s.length);
    char* end = begin;
    foreach(c; s) if (c != stripchar) *(end++) = c;
    write(0, begin, end - begin);
}


@system
void main() {
    string str = "abc;def;ab";
    donttrythisathome(str, ';');
}
December 11, 2021

On Saturday, 11 December 2021 at 09:34:17 UTC, Ola Fosheim Grøstad wrote:

>

@system

Shouldn't be there. Residual leftovers… (I don't want to confuse newbies!)

December 11, 2021

On Saturday, 11 December 2021 at 09:34:17 UTC, Ola Fosheim Grøstad wrote:

>

void donttrythisathome(string s, char stripchar) @trusted {
import core.stdc.stdlib;
char* begin = cast(char*)alloca(s.length);

A function with that name, and calling alloca to boot, cannot be @trusted ;)

December 11, 2021

On Saturday, 11 December 2021 at 09:40:47 UTC, Stanislav Blinov wrote:

>

On Saturday, 11 December 2021 at 09:34:17 UTC, Ola Fosheim Grøstad wrote:

>

void donttrythisathome(string s, char stripchar) @trusted {
import core.stdc.stdlib;
char* begin = cast(char*)alloca(s.length);

A function with that name, and calling alloca to boot, cannot be @trusted ;)

:-)

But I am very trustworthy person! PROMISE!!!

December 11, 2021

Here is mine

  • 0 allocations

  • configurable

  • let's you use it how you wish

  • fast

import std;
void main()
{
    string a = "abc;def;ab";
    writeln("a => ", a);

    foreach(item; split(a, ';'))
        writeln("\t", item);


    string b = "abc;    def   ;ab";
    writeln("a => ", b);

    foreach(item; split(b, ';', SplitOption.TRIM))
        writeln("\t", item);


    string c= "abc;    ;       ;def   ;ab";
    writeln("a => ",c);

    foreach(item; split(c, ';', SplitOption.TRIM | SplitOption.REMOVE_EMPTY))
        writeln("\t", item);
}

SplitIterator!T split(T)(const(T)[] buffer, const(T) delimiter, SplitOption option = SplitOption.NONE)
{
    return SplitIterator!T(buffer, delimiter, option);
}

struct SplitIterator(T)
{
    const(T)[] buffer;
    const(T) delimiter;
    SplitOption option;
    int index = 0;

	int count()
	{
		int c = 0;
		foreach(line; this)
		{
			c++;
		}
		index = 0;
		return c;
	}

	const(T) get(int index)
	{
		return buffer[index];
	}
	
    int opApply(scope int delegate(const(T)[]) dg)
    {
        auto length = buffer.length;
        for (int i = 0; i < length; i++)
        {
            if (buffer[i] == '\0')
            {
                length = i;
                break;
            }
        }

        int result = 0;
        for (int i = index; i < length; i++)
        {
            int entry(int start, int end)
            {
                // trim only if we got something
                if ((end - start > 0) && (option & SplitOption.TRIM))
                {
                    for (int j = start; j < end; j++)
                        if (buffer[j] == ' ')
                            start += 1;
                        else
                            break;
                    for (int k = end; k >= start; k--)
                        if (buffer[k - 1] == ' ')
                            end -= 1;
                        else
                            break;
					
					// nothing left
					if(start >= end) return 0;
                }

				//printf("%i to %i :: %i :: total: %lu\n", start, end, index, buffer.length);
                return dg(buffer[start .. end]) != 0;
            }

            auto c = buffer[i];
            if (c == delimiter)
            {
                if (i == index && (option & SplitOption.REMOVE_EMPTY))
                {
                    // skip if we keep finding the delimiter
                    index = i + 1;
                    continue;
                }

                if ((result = entry(index, i)) != 0)
                    break;

                // skip delimiter for next result
                index = i + 1;
            }

            // handle what's left
            if ((i + 1) == length)
            {
                result = entry(index, i + 1);
            }
        }
        return result;
    }

	// copy from above, only replace if above has changed
    int opApply(scope int delegate(int, const(T)[]) dg)
    {
        auto length = buffer.length;
        for (int i = 0; i < length; i++)
        {
            if (buffer[i] == '\0')
            {
                length = i;
                break;
            }
        }

		int n = 0;
        int result = 0;
        for (int i = index; i < length; i++)
        {
            int entry(int start, int end)
            {
                // trim only if we got something
                if ((end - start > 0) && (option & SplitOption.TRIM))
                {
                    for (int j = start; j < end; j++)
                        if (buffer[j] == ' ')
                            start += 1;
                        else
                            break;
                    for (int k = end; k >= start; k--)
                        if (buffer[k - 1] == ' ')
                            end -= 1;
                        else
                            break;
					
					// nothing left
					if(start >= end) return 0;
                }

				//printf("%i to %i :: %i :: total: %lu\n", start, end, index, buffer.length);
                return dg(n++, buffer[start .. end]) != 0;
            }

            auto c = buffer[i];
            if (c == delimiter)
            {
                if (i == index && (option & SplitOption.REMOVE_EMPTY))
                {
                    // skip if we keep finding the delimiter
                    index = i + 1;
                    continue;
                }

                if ((result = entry(index, i)) != 0)
                    break;

                // skip delimiter for next result
                index = i + 1;
            }

            // handle what's left
            if ((i + 1) == length)
            {
                result = entry(index, i + 1);
            }
        }
        return result;
    }
}


enum SplitOption
{
    NONE = 0,
    REMOVE_EMPTY = 1,
    TRIM = 2
}

December 11, 2021

On Saturday, 11 December 2021 at 14:42:53 UTC, russhy wrote:

>

Here is mine

  • 0 allocations

  • configurable

  • let's you use it how you wish

  • fast

You know that this is already in phobos?

"abc;def;ghi".splitter(';').joiner
December 11, 2021

On Saturday, 11 December 2021 at 18:51:12 UTC, Rumbu wrote:

>

On Saturday, 11 December 2021 at 14:42:53 UTC, russhy wrote:

>

Here is mine

  • 0 allocations

  • configurable

  • let's you use it how you wish

  • fast

You know that this is already in phobos?

"abc;def;ghi".splitter(';').joiner

you need to import a 8k lines of code module that itself imports other modules, and then the code is hard to read

https://github.com/dlang/phobos/blob/v2.098.0/std/algorithm/iteration.d#L2917

December 12, 2021

On Saturday, 11 December 2021 at 19:50:55 UTC, russhy wrote:

>

you need to import a 8k lines of code module that itself imports other modules, and then the code is hard to read

I agree.

@safe:

auto deatheater(char stripchar)(string str) {
	struct voldemort {
        immutable(char)* begin, end;
        bool empty(){ return begin == end; }
        char front(){ return *begin; }
        char back()@trusted{ return *(end-1); }
        void popFront()@trusted{
            while(begin != end){begin++; if (*begin != stripchar) break; }
        }
        void popBack()@trusted{
            while(begin != end){end--; if (*(end-1) != stripchar) break; }
        }
        this(string s)@trusted{
            begin = s.ptr;
            end = s.ptr + s.length;
        }
	}
    return voldemort(str);
}


void main() {
    import std.stdio;
    string str = "abc;def;ab";
    foreach(c; deatheater!';'(str)) write(c);
    writeln();
    foreach_reverse(c; deatheater!';'(str)) write(c);
}