Thread overview
rectifiedIndexOf()
May 05, 2023
Salih Dincer
May 05, 2023
Basile B.
May 05, 2023
Salih Dincer
May 05, 2023
apz28
May 06, 2023
Basile B.
May 05, 2023
apz28
May 06, 2023
Quirin Schroll
May 06, 2023
Salih Dincer
May 12, 2023
Quirin Schroll
May 05, 2023

It has a traditional very beautiful function: indexOf() but it has an important problem: returns -1 when it can't find 'c'...

void main()
{
  enum chr = 'c';
  auto arr = "dlang".dup;
  if(auto res = arr.indexOf(chr))
  {
    assert(arr[res] == chr);
    // core.exception.ArrayIndexError
  }
}

When you try to solve the problem with boolean logic, res is now a bool.

void main()
{
  enum chr = 'd';
  auto arr = "dlang".dup;
  if(auto res = arr.indexOf(chr) > -1)
  {
    assert(arr[res] == chr);
    // core.exception.AssertError
  }
}

We can solve this problem with an alternativeIndexOf, but we're sailing another problem: Rectified Index...

import std.stdio;
void main()
{
  enum chr = 'c';
  auto arr = "dlang".dup;
  if(auto res = arr.rectifiedIndexOf(chr))
  {
    assert(arr[res - 1] == chr);
    res.writefln!"[ --> %s ]";
  } else writeln("Not found!");
}

auto rectifiedIndexOf(A)(A[] arr, A key)
{
  size_t i = 1;
  while(i <= arr.length)
  {
    if(arr[i - 1] == key)
    {
      return i;
    }
    else i++;
  }
  return 0;
}

So now that we've broken the traditional index approach, you need to consider this (res - 1) in your code.

SDB@79

May 05, 2023

On Friday, 5 May 2023 at 00:19:01 UTC, Salih Dincer wrote:

>

It has a traditional very beautiful function: indexOf() but it has an important problem: returns -1 when it can't find 'c'...

void main()
{
  enum chr = 'c';
  auto arr = "dlang".dup;
  if(auto res = arr.indexOf(chr))
  {
    assert(arr[res] == chr);
    // core.exception.ArrayIndexError
  }
}

When you try to solve the problem with boolean logic, res is now a bool.
[...]
So now that we've broken the traditional index approach, you need to consider this (res - 1) in your code.

SDB@79

As often in D you can use a struct to help:

auto toBool(ptrdiff_t t)
{
    struct ToBool
    {
        ptrdiff_t value;

        alias value this;

        bool opCast(T : bool)()
        {
            return value != -1;
        }
    }
    return ToBool(t);
}

void main()
{
      enum chr = 'l';
      auto arr = "dlang".dup;
      if (auto res = arr.indexOf(chr).toBool())
      {
            assert(res == 1);
      }
}

opCast is used for the if condition and the original value for reading back in the array, via alias this.

May 05, 2023

On Friday, 5 May 2023 at 02:43:15 UTC, Basile B. wrote:

>

As often in D you can use a struct to help...
opCast is used for the if condition and the original value for reading back in the array, via alias this.

I haven't seen such an accurate solution from those who replied to me until now. Thanks a lot because very works!

auto toBool(ptrdiff_t t)
{
  struct ToBool
  {
    ptrdiff_t value;

    alias value this;

    bool opCast(T : bool)()
    {
      return value != -1;
    }
  }
  return ToBool(t);
}

void find(char chr)
{
  import std.stdio, std.range,
         std.string : indexOf;

  if(auto res = arr.indexOf(chr).toBool)
  {
    arr.writeln;
    assert(arr[res] == chr);
    ' '.repeat(res).writeln("^--found!");
  } else {
    chr.writeln(" character not found!");
  }
}

auto arr = "dlang".dup;

void main()
{
  foreach(chr; "cad") chr.find();
}

/* Prints:
c character not found!
dlang
  ^--found!
dlang
^--found!
*/

SDB@79

May 05, 2023

On Friday, 5 May 2023 at 00:19:01 UTC, Salih Dincer wrote:

>

It has a traditional very beautiful function: indexOf() but it has an important problem: returns -1 when it can't find 'c'...

void main()
{
  enum chr = 'c';
  auto arr = "dlang".dup;
  if(auto res = arr.indexOf(chr))
  {
    assert(arr[res] == chr);
    // core.exception.ArrayIndexError
  }
}

When you try to solve the problem with boolean logic, res is now a bool.

void main()
{
  enum chr = 'd';
  auto arr = "dlang".dup;
  if(auto res = arr.indexOf(chr) > -1)
  {
    assert(arr[res] == chr);
    // core.exception.AssertError
  }
}

Try below
https://gist.github.com/run-dlang/316a1b9df1c14dade4c5ff13c927cc84

May 05, 2023

On Friday, 5 May 2023 at 14:50:45 UTC, Salih Dincer wrote:

>

On Friday, 5 May 2023 at 02:43:15 UTC, Basile B. wrote:

>

As often in D you can use a struct to help...
opCast is used for the if condition and the original value for reading back in the array, via alias this.

Or as below

struct IndexOfResult
{
    ptrdiff_t value;
    alias value this;

    bool opCast(T : bool)() const @nogc pure @safe
    {
        return value >= 0;
    }
}

void main()
{
    import std.string : indexOf;
    import std.stdio : writeln, writefln;

    enum chr = 'a';
    enum arr = "dlang";
    if (auto res = IndexOfResult(arr.indexOf(chr)))
    {
        assert(arr[res] == chr);
        res.writefln!"[ --> %s ]";
    }
    else
        writeln("Not found!");
}
May 06, 2023

On Friday, 5 May 2023 at 16:51:25 UTC, apz28 wrote:

>

On Friday, 5 May 2023 at 14:50:45 UTC, Salih Dincer wrote:

>

On Friday, 5 May 2023 at 02:43:15 UTC, Basile B. wrote:

>

As often in D you can use a struct to help...
opCast is used for the if condition and the original value for reading back in the array, via alias this.

Or as below

struct IndexOfResult
{
    ptrdiff_t value;
    alias value this;

    bool opCast(T : bool)() const @nogc pure @safe
    {
        return value >= 0;
    }
}

void main()
{
    import std.string : indexOf;
    import std.stdio : writeln, writefln;

    enum chr = 'a';
    enum arr = "dlang";
    if (auto res = IndexOfResult(arr.indexOf(chr)))
    {
        assert(arr[res] == chr);
        res.writefln!"[ --> %s ]";
    }
    else
        writeln("Not found!");
}

Yeah IndexOfResult is a much better name... but you should not change the comparison operator... in theory using !=-1 allows -2, ie size_t.max-1 to be a valid result.

May 06, 2023

On Friday, 5 May 2023 at 00:19:01 UTC, Salih Dincer wrote:

>

It has a traditional very beautiful function: indexOf() but it has an important problem: returns -1 when it can't find 'c'...

That is bad design. It should return an optional type or something like that, but D doesn’t have those. C++ has a lot of similar functions and I guess that was one reason why C++17 introduced an init-statment to if. D could do the same.

if (auto index = arr.indexOf('c'); index >= 0) …
May 06, 2023

On Saturday, 6 May 2023 at 16:39:13 UTC, Quirin Schroll wrote:

>

On Friday, 5 May 2023 at 00:19:01 UTC, Salih Dincer wrote:

>

It has a traditional very beautiful function: indexOf() but it has an important problem: returns -1 when it can't find 'c'...

That is bad design. It should return an optional type or something like that, but D doesn’t have those. C++ has a lot of similar functions and I guess that was one reason why C++17 introduced an init-statment to if. D could do the same.

if (auto index = arr.indexOf('c'); index >= 0) …

In fact, there is worse! The intent is to prevent leaking outside the scope of if-else. But it doesn't work in the else block and we get the error undefined identifier num.

  if(auto num = imported!"std.random".uniform!int.max % 2)
  {
    assert(num > 0);
  } else {
    //assert(num == 0);
  }

I would like these issues to be resolved, but we spend our energy on more difficult things!

SDB@79

May 12, 2023

On Saturday, 6 May 2023 at 18:58:54 UTC, Salih Dincer wrote:

>

On Saturday, 6 May 2023 at 16:39:13 UTC, Quirin Schroll wrote:

>

On Friday, 5 May 2023 at 00:19:01 UTC, Salih Dincer wrote:

>

It has a traditional very beautiful function: indexOf() but it has an important problem: returns -1 when it can't find 'c'...

That is bad design. It should return an optional type or something like that, but D doesn’t have those. C++ has a lot of similar functions and I guess that was one reason why C++17 introduced an init-statment to if. D could do the same.

if (auto index = arr.indexOf('c'); index >= 0) …

In fact, there is worse! The intent is to prevent leaking outside the scope of if-else. But it doesn't work in the else block and we get the error undefined identifier num.

  if(auto num = imported!"std.random".uniform!int.max % 2)
  {
    assert(num > 0);
  } else {
    //assert(num == 0);
  }

I would like these issues to be resolved, but we spend our energy on more difficult things!

I guess the reason why something that is declared in the condition is scoped to the “then” part and not visible in the “else” part is that if something evaluates to false, usually it’s not interesting. Something that evaluates to false is a bool that’s false, a number that’s 0, or a pointer or class handle or whatever reference type that holds no interesting state. A user-defined type evaluates to false and has an interesting value is really bad design.

However, if you do C++’s if with initialization and condition separately, you can handle the “uninteresting false” value in the “then” branch and do the interesting work in the “else” branch. For that reason, something declared in the init-statement of an if should be visible in the “else” branch.