Jump to page: 1 2
Thread overview
What is "FilterResult" type?
Sep 08, 2015
Bahman Movaqar
Sep 08, 2015
cym13
Sep 08, 2015
Bahman Movaqar
Sep 08, 2015
Edwin van Leeuwen
Sep 08, 2015
cym13
Sep 08, 2015
Jonathan M Davis
Sep 09, 2015
Bahman Movaqar
Sep 09, 2015
Sebastiaan Koppe
Sep 09, 2015
Bahman Movaqar
Sep 09, 2015
cym13
Sep 09, 2015
Bahman Movaqar
Sep 09, 2015
cym13
Sep 11, 2015
Bahman Movaqar
Sep 09, 2015
deed
Sep 09, 2015
Atila Neves
Sep 09, 2015
Bahman Movaqar
Sep 08, 2015
Meta
September 08, 2015
From what I can gather the output of `std.algorithm.iteration : filter` is a `FilterResult` type.
I need a bit of help dealing with this type:
  1. Why this type is there in the first place instead of simply using the type of input range?
  2. Where is the documentation for this type?  The documentation always uses `auto` for `filter` return values.
  3. How can I convert this type back into a range?

Thanks.
September 08, 2015
On Tuesday, 8 September 2015 at 09:48:35 UTC, Bahman Movaqar wrote:
> From what I can gather the output of `std.algorithm.iteration : filter` is a `FilterResult` type.
> I need a bit of help dealing with this type:
>   1. Why this type is there in the first place instead of simply using the type of input range?
>   2. Where is the documentation for this type?  The documentation always uses `auto` for `filter` return values.
>   3. How can I convert this type back into a range?
>
> Thanks.

Filter is a template and returns a FilterResult range which is used to lazily compute the result. This behaviour is the same for map and the majority of functions in std.algorithm.

It is an input range really (ie: it has front, popFront and empty). You can chain it just like any other range with other functions and normally don't have to know the exact type (which would be complicated since it is a templated thing).

To store it in a variable if you don't know the exact type you can use auto to use type deduction. If you need the type (for a template for example) you can then use typeof.

Example:

void main() {
    import std.stdio: writeln;
    import std.algorithm: filter, map;

    int[] arr = [1, 2, 3, 4, 5];
    auto result = arr.filter!(x => x%2); // filter out even numbers

    result.writeln; // Writes [1, 3, 5];

    // What is the type of result?
    typeid(result).writeln;
    //-> temporary.main.FilterResult!(__lambda2, int[]).FilterResult

    // Map takes a range, this works because result is one.
    auto result2 = result.map!(x => 2*x);
    //-> temporary.main.MapResult!(__lambda3, FilterResult!(__lambda2,int[])).MapResult

    // Here is the reason you don't want to type them yourself. auto FTW!
    typeid(result).writeln; // main.FilterResult!(__lambda2, int[]).FilterResult

    // You can put it back in an array too
    import std.array: array;
    int[] resultArr = result2.array;
}

If you want more precise documentation the best is to look to the source code (/usr/include/dlang/dmd/std/algorithm/iteration if you are on linux).

September 08, 2015
On Tuesday, 8 September 2015 at 10:08:03 UTC, cym13 wrote:
> Filter is a template and returns a FilterResult range which is used to lazily compute the result. This behaviour is the same for map and the majority of functions in std.algorithm.

Ah...now it makes sense why use a proxy to the results.

> It is an input range really (ie: it has front, popFront and empty). You can chain it just like any other range with other functions and normally don't have to know the exact type (which would be complicated since it is a templated thing).

I agree.  Some types are better left unspecified; like some 300+ characters long type signatures one would get from a generic Scala function :-D

However, I have made this a strict practice of mine to specify the full signature of my public API.  I suppose, if I want to be pedantic, I have to realise the lazy value first and pass the resulting array out.  Is this correct?

> To store it in a variable if you don't know the exact type you can use auto to use type deduction.

True.

> Example:

Great.  The usage is crystal clear to me now.

> If you want more precise documentation the best is to look to the source code (/usr/include/dlang/dmd/std/algorithm/iteration if you are on linux).

Thanks for the help and pointers.

September 08, 2015
On Tuesday, 8 September 2015 at 11:08:59 UTC, Bahman Movaqar wrote:
> On Tuesday, 8 September 2015 at 10:08:03 UTC, cym13 wrote:
>> Filter is a template and returns a FilterResult range which is used to lazily compute the result. This behaviour is the same for map and the majority of functions in std.algorithm.

You can also use .array to (greedily) evaluate the results, which (for filter) will return a range of the same type as went in:

void main()
{
    import std.array : array;
    int[] arr = [1, 2, 3, 4, 5];
    int[] result = arr.filter!(x => x%2).array;
}
September 08, 2015
On Tuesday, 8 September 2015 at 11:08:59 UTC, Bahman Movaqar wrote:
> However, I have made this a strict practice of mine to specify the full signature of my public API.  I suppose, if I want to be pedantic, I have to realise the lazy value first and pass the resulting array out.  Is this correct?

Yes, using an array will work here, but if you are designing a library then I'd prefer that you don't force me into being non lazy where I could be just because you want to have a clear type: laziness is a virtue, reducing possibilities isn't a great idea.

What I would do is a designated wrapper template which will give you a clear type while still being lazy: exactly what FilterResult and MapResult are.
September 08, 2015
On Tuesday, September 08, 2015 11:08:57 Bahman Movaqar via Digitalmars-d-learn wrote:
> However, I have made this a strict practice of mine to specify the full signature of my public API.

If your API returns ranges, that's general not only bad practice but arguably impossible. Most range-based functions purposefully return Voldemort types (the type is declared inside of the function, meaning that you cannot name it). FilterResult really should be to, but I think that it's not in order to work around a bug. Regardless, it's private and should never be used explicitly.

If you want to put a range type in an API, you're forced to do stuff like

typeof(rangeFunc!someFunc(SomeOtherRange.init)) myFunc(int);

If you're returning a range, you should be returning auto. The documentation should then say whether it's an input range, forward range, etc. - but you should pretty much never be using range types explicitly. Your code will become an unmaintainable, unreadable mess if you try. And really, it's unnecessary. Simply having the documentation say what sort of range it's returning is plenty. Exact types are unnecessary in this case and counterproductive.

- Jonathan M Davis

September 08, 2015
On Tuesday, 8 September 2015 at 11:08:59 UTC, Bahman Movaqar wrote:
> However, I have made this a strict practice of mine to specify the full signature of my public API.  I suppose, if I want to be pedantic, I have to realise the lazy value first and pass the resulting array out.  Is this correct?

If you _really_ want to specify the return type, you can import std.range.interfaces[1] and use the appropriate return type (e.g., InputRange!int, RandomAccessFinite!char, etc.) and then append a `.inputRangeObject` to the end of your range chain.

Ex:

import std.algorithm;
import std.range;

InputRange!int doRangeyThings(R)(R r)
if (isInputRange!R)
{
	return r.filter!(x => !(x & 1))
			.map!(x => x * 2)
			.inputRangeObject;
}

void main()
{
	auto n = [1, 2, 3, 4];
	auto m = n.doRangeyThings();
	assert(is(typeof(m) == InputRange!int));
	assert(m.equal([4, 8]));
}

You should use this sparingly, however. The types in std.range.interfaces are interfaces that allow you to combine ranges with runtime polymorphism, and you have to pay the corresponding price for that, namely, you'll be hitting the GC and the virtual functions entail a speed hit.




1. http://dlang.org/phobos/std_range_interfaces.html
September 09, 2015
On Tuesday, 8 September 2015 at 18:45:33 UTC, Jonathan M Davis wrote:
> If you're returning a range, you should be returning auto.

@Jonathan, @cym13 and @Meta
It's reasonable to use `auto`.  However there are times when you need to pass the `auto` value to another function and the receiving function needs to know the type of its input arguments.

In other news:
Consider the following piece of code.  *Please note that I am aware that the simple problem introduced can be solved in several ways which are more efficient.*

    immutable struct FooBar {
      int x;
      int y;
    }
    immutable(FooBar)[] foobars = [
      FooBar(60, 100),
      FooBar(8, 20),
      FooBar(9, 15)
    ];
    int[] nums = [20, 40, 55];

    //////////////////////////////////////////
    // find FooBars which:
    //   - x * y is greater than any `num`
    //   - x and y are smaller than any `num`
    //////////////////////////////////////////
    auto result = reduce!(
      (acc, num) => acc.filter!(
        fb => (fb.x < num && fb.y < num) && (fb.x * fb.y > num)
      )
    )(foobars, nums);
    //
    assert(result[0].x == 9 && result[0].y == 15);

This fails to compile with the following message:

    Error: static assert  "Incompatible function/seed/element: test.__unittestL40_4.__lambda1/immutable(FooBar)[]/int"
    /usr/include/dmd/phobos/std/algorithm/iteration.d(2518):        instantiated from here: reduceImpl!(false, int[], immutable(FooBar)[])
    /usr/include/dmd/phobos/std/algorithm/iteration.d(2502):        instantiated from here: reducePreImpl!(int[], immutable(FooBar)[]) test.d(56):        instantiated from here: reduce!(immutable(FooBar)[], int[])

The only way to make it work is `.array.idup` the output of `filter`.  For example:

    auto result = reduce!(
      (acc, num) => acc.filter!(
        fb => (fb.x < num && fb.y < num) && (fb.x * fb.y > num)
      ).array.idup
    )(foobars, nums);

Honestly, I cannot comprehend anything out of this; why I had to realise the lazy value and `idup` it for it to become usable by `reduce`?
Does this mean that I am simply missing something obvious?
September 09, 2015
On Wednesday, 9 September 2015 at 07:19:06 UTC, Bahman Movaqar wrote:
> The only way to make it work is `.array.idup` the output of `filter`.  For example:
>
>     auto result = reduce!(
>       (acc, num) => acc.filter!(
>         fb => (fb.x < num && fb.y < num) && (fb.x * fb.y > num)
>       ).array.idup
>     )(foobars, nums);
>
> Honestly, I cannot comprehend anything out of this; why I had to realise the lazy value and `idup` it for it to become usable by `reduce`?
> Does this mean that I am simply missing something obvious?

Reduce takes the seed, calls your function, and your function returns a new seed.

What is going wrong is that the types aren't the same. That is, the type of the seed you supplied - `typeof(foobars)` - isn't the type that your function returns - `typeof(acc.filter!...)`.

`array()` fixes that, no idea why you need `idup()` though.

OT: I wonder, is reduce able to call a different templated version of reduce?
September 09, 2015
On Wednesday, 9 September 2015 at 07:59:57 UTC, Sebastiaan Koppe wrote:
> What is going wrong is that the types aren't the same. That is, the type of the seed you supplied - `typeof(foobars)` - isn't the type that your function returns - `typeof(acc.filter!...)`.

Alright.  So, `reduce` initial seed is an `array` while the output of `filter` is a `FilterResult`.  I suppose it makes sense to realise it (`.array`) before passing to the next round.

I was under the impression that `reduce` understands `FilterResult` and realises it internally; it doesn't and I was wrong.  Though I believe it would have been more intuitive if it did.

Thanks for the help.

> `array()` fixes that, no idea why you need `idup()` though.

True --`idup` was not required.  My mistake.


« First   ‹ Prev
1 2