Thread overview
takeBack() implementation in std.range for OutputRanges
Aug 31
monkyyy
August 31

Why have a function called dropBack() but not takeBack()?

Rationale: It uses the drop() function in the module and is compatible with the name dropBack().

Let's say we have a range (called STS for short) that returns odd numbers:

alias STS = SonsuzTekSayı;
struct SonsuzTekSayı { // InfinityNumbers
  import std.traits;
  import std.bigint;

  BigInt numara;
  size_t length;

  this(T)(T ilk) { // Initialize
    static if (isIntegral!T)
      numara = BigInt(ilk % 2 ? ilk : ++ilk);
    else {
      import std.string : isNumeric;
      if (ilk.isNumeric) {
        auto birler = cast(char)ilk.back;
        if (birler % 2 == 0) ++birler;
        ilk.length--;
        numara = BigInt(ilk ~ birler);
      }
    }
  }
  bool empty;
  auto front() => numara;
  auto popFront() => numara += 2;
}

Let's add a convenience function to increase efficiency:

alias adetSayıVer = startingFrom;
auto startingFrom(T)(size_t Length, T First)
{
  import std.range : take;

  auto range = STS(First);
  range.length = Length;

  return range.take(Length);
}

Since our range is not Bidirectional, we get an error with dropBack(). Also, we cannot pop 4 elements from the end like we do in an array (arr[$ - 4..$]), my solution is (if it gives the range length) the following:

auto takeBack(R)(R range, size_t n)
{
  import std.range : drop;

  auto diff = range.length - n;

  return range.drop(diff);
}

void main()
{
  import std.stdio : writeln;
  auto r = 1000.startingFrom("9000");

  r.takeBack(4).writeln; /* PRINTS:

    [10993, 10995, 10997, 10999]

  */
}

FN: Although it is not needed at all, takeBack() already works with an array. Perhaps the slicing method for arrays could be adapted with or without a convenience function.

SDB@79

August 31

On Saturday, 31 August 2024 at 17:55:17 UTC, Salih Dincer wrote:

>

Why have a function called dropBack() but not takeBack()?

probably retro.take is considered good enough? When retro.drop.retro isnt

September 01

On Saturday, 31 August 2024 at 22:29:57 UTC, monkyyy wrote:

>

On Saturday, 31 August 2024 at 17:55:17 UTC, Salih Dincer wrote:

>

Why have a function called dropBack() but not takeBack()?

probably retro.take is considered good enough? When retro.drop.retro isnt

The problem is that, whether retro() or dropBack() works with BidirectionalRange. So we need to find different solutions. If everything was an array, things would be easier, but I don't know how to get the last 4 elements of a range of known length, other than filtering! Does anyone have another practical solution?

SDB@79

September 01
On Sunday, September 1, 2024 3:16:31 PM MDT Salih Dincer via dip.ideas wrote:
> On Saturday, 31 August 2024 at 22:29:57 UTC, monkyyy wrote:
> > On Saturday, 31 August 2024 at 17:55:17 UTC, Salih Dincer wrote:
> >> Why have a function called dropBack() but not takeBack()?
> >
> > probably retro.take is considered good enough? When retro.drop.retro isnt
>
> The problem is that, whether retro() or dropBack() works with BidirectionalRange. So we need to find different solutions. If everything was an array, things would be easier, but I don't know how to get the last 4 elements of a range of known length, other than filtering! Does anyone have another practical solution?

The fact that a range's length is known is not sufficient to be able to get at the elements at its end. In most cases, a range with length is a bidirectional range or random-access range, but it is possible to have a forward range with length (e.g. because takeExactly was used to create it), and the forward range API provides no direct way to get at the elements at its end.

Fundamentally, you cannot get at the end of a range that isn't a bidirectional or random access range without popping off all of the preceding elements. The API gives us no way to do it, and plenty of ranges cannot implement that kind of functionality (otherwise, they'd be bidirectional or random-access ranges).

If you have a random-access range, they're generally sliceable (and probably should require that they be sliceable, but IIRC, they currently don't for some reason), so you can just slice the end (and if for some weird reason a random-access range doesn't have slicing, it's trivial to create a wrapper that does the same thing by using the indices).

And if you have a bidirectional range, then you can use retro and take (though you then have a range in the reverse order and can't use retro on the result, since bidirectional ranges provide no way to access the middle of a range).

If you want to be able to just take the last n elements of a range and have them be in the same order, you need a random-access range. The range API provides no other way to do it, and if a range is not a random-access range, then it's usually because it can't efficiently be one for one reason or another (though in some cases, it's because the programmer didn't bother to add the full range capabilities that they could, because they didn't need them for what they were doing).

If you want to do something like fwdRange.takeBack(5), then that's simply never going to work unless the original range is a random-access range, because that range type doesn't provide a way that that could be implemented efficently.

- Jonathan M Davis