digitalmars.D - Range golf challenge: apply predicate to a subrange, returning the
- FeepingCreature (27/27) Oct 07 2022 AKA "Do My Job For Me Challenge"
- Andrey Zherikov (2/6) Oct 07 2022 May be I misunderstood but why doesn't `.select.map` work here?
- FeepingCreature (3/10) Oct 07 2022 `range.select` returns a subrange of `range`. What's desired is
- FeepingCreature (6/8) Oct 07 2022 For example!
- Andrey Zherikov (5/14) Oct 07 2022 ```d
- Andrey Zherikov (7/11) Oct 07 2022 You can even generalize this:
- =?UTF-8?Q?Ali_=c3=87ehreli?= (13/24) Oct 07 2022 I was trying something similar where I left .map to the user code:
- rassoc (2/3) Oct 07 2022 How about your selector func wraps the elements, similar to Nullable or ...
- FeepingCreature (6/12) Oct 07 2022 Y'all are missing the point a bit. :)
- =?UTF-8?Q?Ali_=c3=87ehreli?= (8/19) Oct 07 2022 Your example does not miss the "missing" elements (the odd ones):
- FeepingCreature (11/37) Oct 08 2022 Right: the *selector* here is `filter!isEven`, which produces a
- H. S. Teoh (44/53) Oct 08 2022 It's actually not that hard, you just have to use std.range.indexed to
- H. S. Teoh (7/23) Oct 08 2022 Merge, i.e., with std.algorithm.multiwayMerge.
- H. S. Teoh (7/13) Oct 07 2022 Another way is if you convert .select into a predicate, then:
AKA "Do My Job For Me Challenge" You have a range. You have an expression that's a chain of `std.algorithm` functions, ie. `filter`, `find`, `until`, `drop`, etc. that select a subset of the range. Let's abbreviate it with `.select`. The subset has the same order of elements as the original range. The only change is that some elements of the original range will be missing. The elements of the range are not mutable, so you cannot mutate the range via `foreach (ref)`. Instead, you have a predicate, let's say `.modify`, that takes a value of the range and returns a new value. Task: Given a range `range`, return a new range that consists of the elements of `range`, except modified with the `modify` predicate at all elements that are part of the subrange selected by `.select`. While ` nogc`. Note: You are allowed to redefine the types of range elements, as long as `select` can see the original value, for instance with `value`. Sketch of a solution: I've been thinking something like "`enumerate` the range, apply selector, "left join zip" (zip where the enumeration matches, to be written) with the original range, apply predicate where left join zip has a match. I'm thinking a syntax like `range.selectSubrange!select.modifySubrange!modify`. Thoughts?
Oct 07 2022
On Friday, 7 October 2022 at 14:00:57 UTC, FeepingCreature wrote:Task: Given a range `range`, return a new range that consists of the elements of `range`, except modified with the `modify` predicate at all elements that are part of the subrange selected by `.select`.May be I misunderstood but why doesn't `.select.map` work here?
Oct 07 2022
On Friday, 7 October 2022 at 14:16:19 UTC, Andrey Zherikov wrote:On Friday, 7 October 2022 at 14:00:57 UTC, FeepingCreature wrote:`range.select` returns a subrange of `range`. What's desired is "`range`, but with the *subrange only* mapped to `modify`."Task: Given a range `range`, return a new range that consists of the elements of `range`, except modified with the `modify` predicate at all elements that are part of the subrange selected by `.select`.May be I misunderstood but why doesn't `.select.map` work here?
Oct 07 2022
On Friday, 7 October 2022 at 15:18:08 UTC, FeepingCreature wrote:`range.select` returns a subrange of `range`. What's desired is "`range`, but with the *subrange only* mapped to `modify`."For example! ``` auto result = 10.iota.selectSubrange!isEven.mapSubrange!"a * 2"; assert(result.equal([0, 1, 4, 3, 8, 5, 12, 7, 16, 9])); ```
Oct 07 2022
On Friday, 7 October 2022 at 15:20:50 UTC, FeepingCreature wrote:On Friday, 7 October 2022 at 15:18:08 UTC, FeepingCreature wrote:```d 10.iota.map!(_ => _.isEven ? _*2 : _); // => [0, 1, 4, 3, 8, 5, 12, 7, 16, 9] ````range.select` returns a subrange of `range`. What's desired is "`range`, but with the *subrange only* mapped to `modify`."For example! ``` auto result = 10.iota.selectSubrange!isEven.mapSubrange!"a * 2"; assert(result.equal([0, 1, 4, 3, 8, 5, 12, 7, 16, 9])); ```
Oct 07 2022
On Friday, 7 October 2022 at 16:32:07 UTC, Andrey Zherikov wrote:```d 10.iota.map!(_ => _.isEven ? _*2 : _); // => [0, 1, 4, 3, 8, 5, 12, 7, 16, 9] ```You can even generalize this: ```d alias mapSubrange(alias filter, alias transform) = map!(_ => filter(_) ? transform(_) : _); 10.iota.mapSubrange!(isEven, _ => _*2); ```
Oct 07 2022
On 10/7/22 09:35, Andrey Zherikov wrote:On Friday, 7 October 2022 at 16:32:07 UTC, Andrey Zherikov wrote:I was trying something similar where I left .map to the user code: import std; bool isEven(T)(T value) { return !(value % 2); } alias applyIf(alias pred, alias func) = value => (pred(value) ? func(value) : value); void main() { auto result = 10.iota.map!(applyIf!(isEven, a => a * 2)); assert(result.equal([0, 1, 4, 3, 8, 5, 12, 7, 16, 9])); } Ali```d 10.iota.map!(_ => _.isEven ? _*2 : _); // => [0, 1, 4, 3, 8, 5, 12, 7, 16, 9] ```You can even generalize this: ```d alias mapSubrange(alias filter, alias transform) = map!(_ => filter(_) ? transform(_) : _); 10.iota.mapSubrange!(isEven, _ => _*2); ```
Oct 07 2022
On 10/7/22 16:00, FeepingCreature via Digitalmars-d wrote:Thoughts?How about your selector func wraps the elements, similar to Nullable or via SumType, and your modifier func operates on the marked ones during iteration while returning the others as is? Poor man's monads?
Oct 07 2022
On Friday, 7 October 2022 at 16:49:33 UTC, rassoc wrote:On 10/7/22 16:00, FeepingCreature via Digitalmars-d wrote:Y'all are missing the point a bit. :) The type spec of the selector is "takes a range, returns a subrange." A subrange here being "a range like the original but with some amount of elements missing." Not, specifically, a predicate! That's what makes it interesting! :)Thoughts?How about your selector func wraps the elements, similar to Nullable or via SumType, and your modifier func operates on the marked ones during iteration while returning the others as is? Poor man's monads?
Oct 07 2022
On 10/7/22 12:19, FeepingCreature wrote:On Friday, 7 October 2022 at 16:49:33 UTC, rassoc wrote:It's because of your example! :)On 10/7/22 16:00, FeepingCreature via Digitalmars-d wrote:Y'all are missing the point a bit. :)Thoughts?How about your selector func wraps the elements, similar to Nullable or via SumType, and your modifier func operates on the marked ones during iteration while returning the others as is? Poor man's monads?The type spec of the selector is "takes a range, returns a subrange." A subrange here being "a range like the original but with some amount of elements missing."Your example does not miss the "missing" elements (the odd ones): assert(result.equal([0, 1, 4, 3, 8, 5, 12, 7, 16, 9])); Maybe you mean selectSubrange is already written and does miss some elements and the goal is to merge those back into where they were missing from. Ali
Oct 07 2022
On Friday, 7 October 2022 at 20:19:07 UTC, Ali Çehreli wrote:On 10/7/22 12:19, FeepingCreature wrote:Right: the *selector* here is `filter!isEven`, which produces a range of `0, 2, 4, 6, 8` or variations thereof. The point is to get a range where every element that `filter!isEven` selected has been modified by `square`, while *keeping* the unselected elements in the same order. And that's fairly straightforward for `filter!isEven` because `isEven` is a predicate. But, for instance, `find` or `until` aren't predicates. So in a way, the question is "how do you predicatize an arbitrary subrange selection expression."On Friday, 7 October 2022 at 16:49:33 UTC, rassoc wrote:NullableOn 10/7/22 16:00, FeepingCreature via Digitalmars-d wrote:Thoughts?How about your selector func wraps the elements, similar tomarked onesor via SumType, and your modifier func operates on theman's monads?during iteration while returning the others as is? PoorY'all are missing the point a bit. :)It's because of your example! :)The type spec of the selector is "takes a range, returns asubrange." Asubrange here being "a range like the original but with someamount ofelements missing."Your example does not miss the "missing" elements (the odd ones):assert(result.equal([0, 1, 4, 3, 8, 5, 12, 7, 16, 9])); Maybe you mean selectSubrange is already written and does miss some elements and the goal is to merge those back into where they were missing from. AliYes, exactly.
Oct 08 2022
On Sat, Oct 08, 2022 at 05:08:17PM +0000, FeepingCreature via Digitalmars-d wrote: [...]Right: the *selector* here is `filter!isEven`, which produces a range of `0, 2, 4, 6, 8` or variations thereof. The point is to get a range where every element that `filter!isEven` selected has been modified by `square`, while *keeping* the unselected elements in the same order. And that's fairly straightforward for `filter!isEven` because `isEven` is a predicate. But, for instance, `find` or `until` aren't predicates. So in a way, the question is "how do you predicatize an arbitrary subrange selection expression."It's actually not that hard, you just have to use std.range.indexed to tag the selected range, then merge the original with the modified range. Here's a sketch of my idea: - Pair the range with an index using .indexed, pass that through .select to get the filtered range. - Use .map to map it to the modified elements. - Furthermore, tag each element with a secondary index of 0 (explained below). - Pair the original range with .indexed, plus a secondary index of 1. - Merge the two ranges together so that the result is lexicographically sorted according the indexing order (i.e., filtered elements will appear in the right positions in the original range) and the secondary index (i.e., replaced elements appear first in the resulting range). - Pass the merged range to .uniq to replace the filtered values with their modified counterparts, with uniqueness predicate defined by the (first) index. This effectively drops elements whose secondary index is 1 if they have a replacement, otherwise they are left untouched. - Unwrap the elements to drop the indices to obtain the final result. // Proof-of-concept example: origRange: A B C D E F G H origRange.select: B F G Replacement values for .select: X Y Z origRange indexed, plus secondary index of 1: (0,1,A) (1,1,B) (2,1,C) (3,1,D) (4,1,E) (5,1,F) (6,1,G) (7,1,H) Selected range indexed, plus secondary index of 2: (1,0,B) (5,0,F) (6,0,G) Selected range with modified values substituted using .map: (1,0,X) (5,0,Y) (6,0,Z) Merge the two ranges in lexicographic order of first two elements: (0,1,A) (1,0,X) (1,1,B) (2,1,C) (3,1,D) (4,1,E) (5,0,Y) (5,1,F) (6,0,Z) (6,1,G) (7,1,H) Pass merged range through .uniq, with equivalence defined solely by first index (first element with a particular index gets picked up, subsequent elements with the same index are dropped): (0,1,A) (1,0,X) (2,1,C) (3,1,D) (4,1,E) (5,0,Y) (6,0,Z) (7,1,H) Unwrap the range: A X C D E Y Z H QED. ;-) T -- Javascript is what you use to allow third party programs you don't know anything about and doing you know not what to run on your computer. -- Charles Hixson
Oct 08 2022
On Sat, Oct 08, 2022 at 12:45:29PM -0700, H. S. Teoh via Digitalmars-d wrote: [...]Here's a sketch of my idea: - Pair the range with an index using .indexed, pass that through .select to get the filtered range. - Use .map to map it to the modified elements. - Furthermore, tag each element with a secondary index of 0 (explained below). - Pair the original range with .indexed, plus a secondary index of 1. - Merge the two ranges together so that the result is lexicographically sorted according the indexing order (i.e., filtered elements will appear in the right positions in the original range) and the secondary index (i.e., replaced elements appear first in the resulting range).Merge, i.e., with std.algorithm.multiwayMerge.- Pass the merged range to .uniq to replace the filtered values with their modified counterparts, with uniqueness predicate defined by the (first) index. This effectively drops elements whose secondary index is 1 if they have a replacement, otherwise they are left untouched. - Unwrap the elements to drop the indices to obtain the final result.[...] T -- Do not reason with the unreasonable; you lose by definition.
Oct 08 2022
On Fri, Oct 07, 2022 at 04:49:33PM +0000, rassoc via Digitalmars-d wrote:On 10/7/22 16:00, FeepingCreature via Digitalmars-d wrote:Another way is if you convert .select into a predicate, then: auto r = myrange.map!(e => pred(e) ? modify(e) : e); But that depends on how easy it is to convert .select into a predicate. T -- If lightning were to ever strike an orchestra, it'd always hit the conductor first.Thoughts?How about your selector func wraps the elements, similar to Nullable or via SumType, and your modifier func operates on the marked ones during iteration while returning the others as is? Poor man's monads?
Oct 07 2022