digitalmars.dip.ideas - takeBack() implementation in std.range for OutputRanges
- Salih Dincer (66/66) Aug 31 2024 Why have a function called dropBack() but not takeBack()?
- monkyyy (3/4) Aug 31 2024 probably retro.take is considered good enough? When
- Salih Dincer (7/11) Sep 01 2024 The problem is that, whether retro() or dropBack() works with
- Jonathan M Davis (33/44) Sep 01 2024 The fact that a range's length is known is not sufficient to be able to ...
- Salih Dincer (50/50) Nov 22 Last year, we have been examining a significant design question
- Paul Backus (4/9) Nov 27 Please don't post LLM-generated text on the forums. It's
- Salih Dincer (14/19) Nov 22 You are right, it is impossible to implement ```takeBack```
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:
```d
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:
```d
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:
```d
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
Aug 31 2024
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
Aug 31 2024
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: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 79Why have a function called dropBack() but not takeBack()?probably retro.take is considered good enough? When retro.drop.retro isnt
Sep 01 2024
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: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 DavisOn Saturday, 31 August 2024 at 17:55:17 UTC, Salih Dincer wrote: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?Why have a function called dropBack() but not takeBack()?probably retro.take is considered good enough? When retro.drop.retro isnt
Sep 01 2024
Last year, we have been examining a significant design question within the context of range functionality: This critical analysis aims to provide a deeper look into the D language's range philosophy, clarifying the issue with the following evidence and rationale: **Symmetry and Interface Integrity** **Rationale:** Functions like ```take()``` and ```drop()``` in our standard library module ```std.range``` handle operations from the front, while ```dropBack()``` allows for removing elements from the end. Logically, having a ```takeBack()``` function to take elements from the end would complete the interface symmetrically, improving code readability and offering users complete uniformity. **D Language Range Philosophy and Performance Concerns** The core design of D language ranges (being lazy and potentially infinite) is the fundamental obstacle preventing the inclusion of ```takeBack()``` as a standard function: **Evidence 1:** ```SizedRange``` Requirement: The proposed ```takeBack(R range, size_t n) => range.drop(range.length - n)``` implementation inherently requires the range's length (```.length```) to be known. This mandates that the range conforms to the SizedRange concept. Infinite or length-unknown ranges cannot use this function. **Evidence 2:** ```BidirectionalRange``` Preference: The D library typically prefers the BidirectionalRange requirement for end-of-range operations (such as ```popBack``` or ```dropBack```). This guarantees that access to the end is both reliable and efficient. For Forward Ranges, calculating ```.length``` may necessitate consuming (iterating over) the entire range. This contradicts the philosophy of lazy evaluation and introduces a potentially detrimental performance cost. To avoid the risk of such cost without knowing the range's type, the library restricts functions focused on the back end to stricter range types (usually BidirectionalRange or higher). The absence of a ```takeBack``` function in the D standard library is largely due to the emphasis on performance and laziness in D's range design. The library prioritises BidirectionalRange for end-of-range operations to ensure efficiency. However, for ranges that accept the ```SizedRange``` constraint, the proposed single-line implementation (```range.drop(range.length - n)```) is extremely clean, useful, and practical. This makes it a valid proposal for consideration as a utility function to be added to the standard library, specifically for Sized Forward Ranges. This discussion once again highlights how philosophical decisions and practical needs intersect in our library development process. We welcome your input and potential Pull Requests on this matter. Yours faithfully,
Nov 22
On Sunday, 23 November 2025 at 00:07:53 UTC, Salih Dincer wrote:Last year, we have been examining a significant design question within the context of range functionality: This critical analysis aims to provide a deeper look into the D language's range philosophy, clarifying the issue with the following evidence and rationale:Please don't post LLM-generated text on the forums. It's disrespectful to all of the people who take the time to read and respond to posts here in good faith.
Nov 27
On Monday, 2 September 2024 at 02:25:24 UTC, Jonathan M Davis wrote: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 DavisYou are right, it is impossible to implement ```takeBack``` efficiently on a generic ```ForwardRange```. However, my proposal explicitly imposes the requirement of a ```SizedRange```, where the length is known beforehand. Within this constrained scope, the function is essentially a call to ```drop()``` ```(range.drop(range.length - n)) ```. Our goal is to add a convenience function that facilitates this manual calculation, which is already valid, by providing a naming symmetry with ```dropBack```. In the current library, there is a lack of an intuitive way to cleanly take the end of ranges that are not ```BidirectionalRange``` but whose length is known. SDB 79
Nov 22









Paul Backus <snarwin gmail.com> 