Jump to page: 1 2
Thread overview
countUntil to print all the index of a given string.
Feb 18, 2018
Vino
Feb 18, 2018
Cym13
Feb 18, 2018
Cym13
Feb 19, 2018
psychoticRabbit
Feb 20, 2018
aberba
Feb 20, 2018
Jonathan M Davis
Feb 24, 2018
Vino
Feb 25, 2018
Seb
Feb 25, 2018
Jonathan M Davis
Feb 25, 2018
Seb
Feb 25, 2018
Jonathan M Davis
Feb 25, 2018
Vino
Feb 26, 2018
Andrew
February 18, 2018
Hi All,

  Request your help on printing the all index of an array element , eg; the below code prints the index of the string "Test2" as [1], but the string "Test2" is present 2 times at  index  1 and 4, so how do I print all the index of a given string.

import std.stdio;
import std.container;
import std.algorithm;

void main () {
auto a = Array!string("Test1", "Test2", "Test3", "Test1", "Test2");
writeln(SList!int(a[].countUntil("Test2"))[]);
}

Output
[1]

Expected
[1, 4]

From,
Vino.B
February 18, 2018
On Sunday, 18 February 2018 at 11:55:37 UTC, Vino wrote:
> Hi All,
>
>   Request your help on printing the all index of an array element , eg; the below code prints the index of the string "Test2" as [1], but the string "Test2" is present 2 times at  index  1 and 4, so how do I print all the index of a given string.
>
> import std.stdio;
> import std.container;
> import std.algorithm;
>
> void main () {
> auto a = Array!string("Test1", "Test2", "Test3", "Test1", "Test2");
> writeln(SList!int(a[].countUntil("Test2"))[]);
> }
>
> Output
> [1]
>
> Expected
> [1, 4]
>
> From,
> Vino.B

countUntil is good when you want to avoid having to look at all your data, but in this case I don't think it's the best solution. You could do a loop storing each index and then restart your countUntil from there, but quite frankly it would be easier to just loop over the array at that point:

    ulong[] result;
    for (ulong index=0 ; index<a.length ; index++)
        if (a[index] == "Test2")
            result ~= index;
    writeln(result);

You could also use enumerate to make this a tad easier:

    import std.range: enumerate;

    ulong[] result;
    foreach (index, value ; a[].enumerate)
        if (a[index] == "Test2")
        result ~= index;
    writeln(result);

However, if you want a more streamlined, functionnal solution, you can go all the way and avoid all explicit loops and intermediate variables using fold:

    import std.range: enumerate;
    import std.algorithm: fold;

    a[]
     .enumerate
     .fold!((a, b) => b[1] == "Test2" ? a ~ b[0] : a)(cast(ulong[])[])
     .writeln;


February 18, 2018
On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote:
> [...]

Just thought of a much better/simpler solution for that last case that also doesn't force you to read all data (which might be impossible when dealing with infinite ranges):

    import std.range;
    import std.algorithm;

    a[]
     .enumerate                       // get tuples (index, value)
     .filter!(t => t[1] == "Test2")   // keep only if value == "Test2"
     .map!(t => t[0])                 // keep only the index part
     .writeln;

Completely lazy.

February 19, 2018
On Sunday, 18 February 2018 at 15:23:14 UTC, Cym13 wrote:
> On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote:
>> [...]
>
> Just thought of a much better/simpler solution for that last case that also doesn't force you to read all data (which might be impossible when dealing with infinite ranges):
>
>     import std.range;
>     import std.algorithm;
>
>     a[]
>      .enumerate                       // get tuples (index, value)
>      .filter!(t => t[1] == "Test2")   // keep only if value == "Test2"
>      .map!(t => t[0])                 // keep only the index part
>      .writeln;
>
> Completely lazy.

A nice example of how D's multiparadigm programming model allows the utilisation of algorithms that can scale to process massive datasets.

There is an article in there somewhere ;-)

February 20, 2018
On Sunday, 18 February 2018 at 15:23:14 UTC, Cym13 wrote:
> On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote:
>> [...]
>
> Just thought of a much better/simpler solution for that last case that also doesn't force you to read all data (which might be impossible when dealing with infinite ranges):
>
>     import std.range;
>     import std.algorithm;
>
>     a[]
>      .enumerate                       // get tuples (index, value)
>      .filter!(t => t[1] == "Test2")   // keep only if value == "Test2"
>      .map!(t => t[0])                 // keep only the index part
>      .writeln;
>
> Completely lazy.

How does one detect an operation as lazy or not?  Is the some compile-time or runtime check for that?

My guess is by referring to the docs function signature.
February 20, 2018
On Tuesday, February 20, 2018 08:44:37 aberba via Digitalmars-d-learn wrote:
> On Sunday, 18 February 2018 at 15:23:14 UTC, Cym13 wrote:
> > On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote:
> >> [...]
> >
> > Just thought of a much better/simpler solution for that last case that also doesn't force you to read all data (which might
> >
> > be impossible when dealing with infinite ranges):
> >     import std.range;
> >     import std.algorithm;
> >
> >     a[]
> >
> >      .enumerate                       // get tuples (index,
> >
> > value)
> >
> >      .filter!(t => t[1] == "Test2")   // keep only if value ==
> >
> > "Test2"
> >
> >      .map!(t => t[0])                 // keep only the index
> >
> > part
> >
> >      .writeln;
> >
> > Completely lazy.
>
> How does one detect an operation as lazy or not?  Is the some compile-time or runtime check for that?
>
> My guess is by referring to the docs function signature.

You have to read the docs or read the source code, though in general, functions that return a range type that wraps the original range tend to be lazy, whereas if a function returns the original range type or an array, then it's clearly not lazy.

- Jonathan M Davis

February 24, 2018
On Tuesday, 20 February 2018 at 08:50:27 UTC, Jonathan M Davis wrote:
> On Tuesday, February 20, 2018 08:44:37 aberba via Digitalmars-d-learn wrote:
>> On Sunday, 18 February 2018 at 15:23:14 UTC, Cym13 wrote:
>> > On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote:
>> >> [...]
>> >
>> > Just thought of a much better/simpler solution for that last case that also doesn't force you to read all data (which might
>> >
>> > be impossible when dealing with infinite ranges):
>> >     import std.range;
>> >     import std.algorithm;
>> >
>> >     a[]
>> >
>> >      .enumerate                       // get tuples (index,
>> >
>> > value)
>> >
>> >      .filter!(t => t[1] == "Test2")   // keep only if value ==
>> >
>> > "Test2"
>> >
>> >      .map!(t => t[0])                 // keep only the index
>> >
>> > part
>> >
>> >      .writeln;
>> >
>> > Completely lazy.
>>
>> How does one detect an operation as lazy or not?  Is the some compile-time or runtime check for that?
>>
>> My guess is by referring to the docs function signature.
>
> You have to read the docs or read the source code, though in general, functions that return a range type that wraps the original range tend to be lazy, whereas if a function returns the original range type or an array, then it's clearly not lazy.
>
> - Jonathan M Davis

Hi All,

 Thank you very much, the provide solution work's for the given example, so how can we achieve the same for the below code


import std.stdio;
import std.container;
import std.algorithm;

void main () {
auto a = Array!string("Test1", "Test2", "Test3", "Test1", "Test2");
auto b = Array!string("Test1", "Test2", "Test3");

foreach(i; b[]) {
writeln(SList!int(a[].countUntil(i))[]); }
}

Output
[0]
[1]
[2]

Required
[0,3]
[1,4]
[2]

From,
Vino.B
February 25, 2018
On Tuesday, 20 February 2018 at 08:44:37 UTC, aberba wrote:
> On Sunday, 18 February 2018 at 15:23:14 UTC, Cym13 wrote:
>> On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote:
>>> [...]
>>
>> Just thought of a much better/simpler solution for that last case that also doesn't force you to read all data (which might be impossible when dealing with infinite ranges):
>>
>>     import std.range;
>>     import std.algorithm;
>>
>>     a[]
>>      .enumerate                       // get tuples (index, value)
>>      .filter!(t => t[1] == "Test2")   // keep only if value == "Test2"
>>      .map!(t => t[0])                 // keep only the index part
>>      .writeln;
>>
>> Completely lazy.
>
> How does one detect an operation as lazy or not?  Is the some compile-time or runtime check for that?
>
> My guess is by referring to the docs function signature.

While it's not a replacement for checking the code manually, @nogc helps a lot:


---
void main() @nogc
{
    import std.container, std.stdio;
    auto a = Array!string("Test1", "Test2", "Test3", "Test1", "Test2");
    import std.algorithm, std.range;
    auto r = a[].enumerate.filter!(t => t[1] == "Test2").map!(t => t[0]);
    debug r.writeln; // std.stdio.writeln allocates at the moment
}
---

https://run.dlang.io/is/Fo32sN
February 24, 2018
On Sunday, February 25, 2018 01:49:05 Seb via Digitalmars-d-learn wrote:
> On Tuesday, 20 February 2018 at 08:44:37 UTC, aberba wrote:
> > On Sunday, 18 February 2018 at 15:23:14 UTC, Cym13 wrote:
> >> On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote:
> >>> [...]
> >>
> >> Just thought of a much better/simpler solution for that last case that also doesn't force you to read all data (which might
> >>
> >> be impossible when dealing with infinite ranges):
> >>     import std.range;
> >>     import std.algorithm;
> >>
> >>     a[]
> >>
> >>      .enumerate                       // get tuples (index,
> >>
> >> value)
> >>
> >>      .filter!(t => t[1] == "Test2")   // keep only if value ==
> >>
> >> "Test2"
> >>
> >>      .map!(t => t[0])                 // keep only the index
> >>
> >> part
> >>
> >>      .writeln;
> >>
> >> Completely lazy.
> >
> > How does one detect an operation as lazy or not?  Is the some compile-time or runtime check for that?
> >
> > My guess is by referring to the docs function signature.
>
> While it's not a replacement for checking the code manually, @nogc helps a lot:

That doesn't actually tell you whether the range is lazy. It just tells you whether any allocations may occur. If any exceptions could be thrown, then a lazy solution can't be @nogc (something that's often the case with strings thanks to auto-decoding and UTFExceptions), and a solution could be eager without allocating if the result doesn't require any allocation. Also, you could have a lazy range that involves a lambda that allocates a closure.

So, yeah, a lot of the time, @nogc means that the range is lazy, but it doesn't guarantee it, and not being able to be @nogc doesn't mean that it's eager. So, I'd argue that while @nogc gives you a clue, it's ultimately a pretty poor way to try and figure out whether a range is lazy or not. All it really tells you is whether it's guaranteed that no allocations will occur on the GC heap.

- Jonathan M Davis

February 25, 2018
On Sunday, 25 February 2018 at 02:37:00 UTC, Jonathan M Davis wrote:
> If any exceptions could be thrown, then a lazy solution can't be @nogc (something that's often the case with strings thanks to auto-decoding and UTFExceptions), and a solution could be eager without allocating if the result doesn't require any allocation.

FYI -dip1008 is a thing now and part of 2.079.
See also:

https://dlang.org/changelog/pending.html#dip1008
https://run.dlang.io/is/clNX6G
https://github.com/dlang/DIPs/blob/master/DIPs/DIP1008.md


> Also, you could have a lazy range that involves a lambda that allocates a closure.

Of course, or a @nogc range that allocates with malloc or eagerly steps through the entire range.

Anyhow I just mentioned it because it's the best form of automatic checking that we have (what the OP was asking for) and in many cases when an algorithm can't be @nogc it does allocate somewhere which is a red flag.
« First   ‹ Prev
1 2