View mode: basic / threaded / horizontal-split · Log in · Help
August 25, 2012
Lazy range of hashes?
I could use something like this:

void main()
{
   int[string] x = ["foo":1];
   int[string] y = ["bar":1];
   assert("bar" in lazyHash(x, y));
}

Essentially it would turn into lazy 'in' checks, meaning first opIn_r
would be called for 'x', and then for 'y'.

Otherwise it might be expensive to have to create a new hash that has
all the keys and values of other hashes. Has anyone ever implemented
this?
August 26, 2012
Re: Lazy range of hashes?
Andrej Mitrovic:

> void main()
> {
>     int[string] x = ["foo":1];
>     int[string] y = ["bar":1];
>     assert("bar" in lazyHash(x, y));
> }
>
> Essentially it would turn into lazy 'in' checks, meaning first 
> opIn_r
> would be called for 'x', and then for 'y'.

This seems to work:

import std.algorithm;
void main() {
    auto x = ["foo": 1];
    auto y = ["bar": 2];
    //assert("bar" in lazyHash(x, y));
    assert(any!(h => "bar" in h)([x, y]));
}

Recently I have seen a lazy range struct able to replace that
[x,y]. I don't know if it's in Phobos already.

Bye,
bearophile
August 26, 2012
Re: Lazy range of hashes?
On 8/26/12, bearophile <bearophileHUGS@lycos.com> wrote:
> This seems to work:

It seems it's as simple as defining a struct:

struct LazyHash(T...)
{
   T hashes;

   bool opIn_r(X)(X x)
   {
       foreach (hash; hashes)
       {
           if (x in hash)
               return true;
       }

       return false;
   }
}

auto lazyHash(T...)(T t)
{
   return LazyHash!T(t);
}

void main()
{
   auto x = ["foo" : 1];
   auto y = ["bar" : 2];

   assert("bar" in lazyHash(x, y));
   assert("barx" !in lazyHash(x, y));
}

I didn't even have to define a ctor since field assignment works.
August 26, 2012
Re: Lazy range of hashes?
On 08/25/2012 05:30 PM, Andrej Mitrovic wrote:
> On 8/26/12, bearophile<bearophileHUGS@lycos.com>  wrote:
>> This seems to work:
>
> It seems it's as simple as defining a struct:
>
> struct LazyHash(T...)
> {
>      T hashes;
>
>      bool opIn_r(X)(X x)
>      {
>          foreach (hash; hashes)
>          {
>              if (x in hash)
>                  return true;
>          }
>
>          return false;
>      }
> }
>
> auto lazyHash(T...)(T t)
> {
>      return LazyHash!T(t);
> }
>
> void main()
> {
>      auto x = ["foo" : 1];
>      auto y = ["bar" : 2];
>
>      assert("bar" in lazyHash(x, y));
>      assert("barx" !in lazyHash(x, y));
> }
>
> I didn't even have to define a ctor since field assignment works.

Cool! :) If the operator returns the pointer to the element, then the 
callers can access its value as well:

    auto opIn_r(X)(X x)
    {
        foreach (hash; hashes)
        {
            auto p = x in hash;
            if (p)
                return p;    // <-- return the pointer
        }

        return null;
    }

// ...

    import std.stdio;

    auto p = "foo" in lazyHash(x, y);
    if (p) {
        writeln(*p);    // <-- the user can access the value
    }

Ali
August 26, 2012
Re: Lazy range of hashes?
On Sunday, 26 August 2012 at 07:29:13 UTC, Ali Çehreli wrote:
> Cool! :) If the operator returns the pointer to the element, 
> then the callers can access its value as well

 Reminds me, although not for you Ali, but as a pointer return a 
question came up. I've wondered and haven't tested this but 
hypothetically:

 Due to that you can call structures and class members even from 
a pointer (transparently compared to C/C++), does it 
automatically convert from a pointer to a non-pointer type if the 
return calls for it? In a class that's irrelevant (it's already a 
reference type and should auto-fix itself); But a struct or 
non-class?

 //just to get the idea
 //potentially ref int, rather than int* as well
 int getSomeValue(int[string] x, string someValue) {
   auto sv = someValue in x;
   return x ? x : 0; //auto convert? Or error? If not, why?
 }

 Rather than auto could ref work? Or if it's a exact valueType 
(with postblitz) or (known to be relocatable) would it make a new 
copy? I can see the importance of both, but depending on the 
return type in cases, having it throw an error seems like the 
best policy if it's a non-built in, since it's trivial changing 
return x, with return *x; Also as a built in type it could 
automatically do the copy/conversion for you.
August 26, 2012
Re: Lazy range of hashes?
On 08/26/2012 12:55 AM, Era Scarecrow wrote:
> On Sunday, 26 August 2012 at 07:29:13 UTC, Ali Çehreli wrote:
>> Cool! :) If the operator returns the pointer to the element, then the
>> callers can access its value as well
>
> Reminds me, although not for you Ali, but as a pointer return a question
> came up. I've wondered and haven't tested this but hypothetically:
>
> Due to that you can call structures and class members even from a
> pointer (transparently compared to C/C++),

It has been argued that the -> operator has not been needed for C anyway.

Interestingly, it has some value in C++ because if a type has defined 
operator->() (rather, for this discussion, operator.(), which does not 
exist in C++ today), then the following would cause confusion for types 
that worked like smart pointers:

class P
{
    /* hypothetical operator */
    T * operator.();
    void foo();
};

    P p = bar();
    p.foo(); // foo() of the pointer type or the 'pointed to type'?

D does not have that question because the dot operator may not be 
overloaded. (opDot() has been (will be?) deprecated.)

> does it automatically convert
> from a pointer to a non-pointer type if the return calls for it?

No: The dot does not convert the type. The dot has different meanings on 
structs vs. classes. With structs, it always operates on the struct object:

    o.sizeof   // The size of the struct object
    o.foo()    // The member of the struct object

With classes, it operates sometimes on the reference and sometimes on 
the referenced object:

    o.sizeof   // The size of the class reference
    o.foo()    // The member of the class object

That's D's way of confusing on this topic.

> In a
> class that's irrelevant (it's already a reference type and should
> auto-fix itself); But a struct or non-class?
>
> //just to get the idea
> //potentially ref int, rather than int* as well
> int getSomeValue(int[string] x, string someValue) {
> auto sv = someValue in x;
> return x ? x : 0; //auto convert? Or error? If not, why?

You meant sv:

    return sv ? sv : 0;

That is still a compilation error for a statically-typed language like 
D. The types of sv and 0 don't match. But the line should always be like 
this anyway:

    return sv ? *sv : 0;

Because sv is always a pointer:

  // For a struct pointer:
  *sv  // a reference to the struct object

  // For a class reference:
  *sv is another reference to the actual class object

> Rather than auto could ref work?

You mean, 'ref' on the return type, right? For opIn_r, it better not be 
'ref', because then it would be returning a reference to a local pointer:

    // Note ref return. I think this is a bug.
    ref opIn_r(X)(X x)
    {
        foreach (hash; hashes)
        {
            auto p = x in hash;
            if (p)
                return p; // <-- return a reference to the local pointer
        }

        return null;
    }

I think that is a bug but the compiler does not give a warning about 
returning a reference to the local pointer.

> Or if it's a exact valueType (with
> postblitz) or (known to be relocatable) would it make a new copy? I can
> see the importance of both, but depending on the return type in cases,
> having it throw an error seems like the best policy if it's a non-built
> in, since it's trivial changing return x, with return *x; Also as a
> built in type it could automatically do the copy/conversion for you.

As you said, returning *x would return a copy of the value if it were a 
struct.

Ali
August 26, 2012
Re: Lazy range of hashes?
On 08/26/12 17:28, Ali Çehreli wrote:
> On 08/26/2012 12:55 AM, Era Scarecrow wrote:
>> On Sunday, 26 August 2012 at 07:29:13 UTC, Ali Çehreli wrote:
>>> Cool! :) If the operator returns the pointer to the element, then the
>>> callers can access its value as well
>>
>> Reminds me, although not for you Ali, but as a pointer return a question
>> came up. I've wondered and haven't tested this but hypothetically:
>>
>> Due to that you can call structures and class members even from a
>> pointer (transparently compared to C/C++),
> 
> It has been argued that the -> operator has not been needed for C anyway.
> 
> Interestingly, it has some value in C++ because if a type has defined operator->() (rather, for this discussion, operator.(), which does not exist in C++ today), then the following would cause confusion for types that worked like smart pointers:
> 
> class P
> {
>     /* hypothetical operator */
>     T * operator.();
>     void foo();
> };
> 
>     P p = bar();
>     p.foo(); // foo() of the pointer type or the 'pointed to type'?
> 
> D does not have that question because the dot operator may not be overloaded. (opDot() has been (will be?) deprecated.)

opDispatch lets you write the equivalent in D [1]. The 'local' member always has
to take precedence over any 'remote' ones (you can hide (rename/wrap etc) the
local one if you want, doing it the other way wont work).

>> does it automatically convert
>> from a pointer to a non-pointer type if the return calls for it?
> 
> No: The dot does not convert the type. The dot has different meanings on structs vs. classes. With structs, it always operates on the struct object:
> 
>     o.sizeof   // The size of the struct object
>     o.foo()    // The member of the struct object
> 
> With classes, it operates sometimes on the reference and sometimes on the referenced object:
> 
>     o.sizeof   // The size of the class reference
>     o.foo()    // The member of the class object
> 
> That's D's way of confusing on this topic.

The dot means the same thing, you just have to remember that a class reference is
actually a pointer to the class instance. IOW 'o.whatever' for classes works just
like 'p.whatever' does for structs.

What is confusing is the wrong reference_type-to-pointer model, but fixing it up
right now is too late. Or maybe not, given the tiny amount of D code out there and
the fact that only code dealing with pointers-to-classes would be affected. Such
code is probably so rare that the cost could still be acceptable.

artur

[1] Well, almost - there are things like dealing with '@property' which need
   improvements. And of course 'alias this' can be used, sometimes.
August 26, 2012
Re: Lazy range of hashes?
On Sunday, 26 August 2012 at 15:28:17 UTC, Ali Çehreli wrote:
> On 08/26/2012 12:55 AM, Era Scarecrow wrote:

> Interestingly, it has some value in C++ because if a type has 
> defined operator->() (rather, for this discussion, operator.(), 
> which does not exist in C++ today), then the following would 
> cause confusion for types that worked like smart pointers:

> D does not have that question because the dot operator may not 
> be overloaded. (opDot() has been (will be?) deprecated.)

 Since D wouldn't ever have a need to emulate pointers (like 
Iterators for C++) the -> and . overloading seem moot. Some 
features are best not to be implemented as they would give more 
trouble than they're worth.

> No: The dot does not convert the type. The dot has different 
> meanings on structs vs. classes. With structs, it always 
> operates on the struct object:
>
>     o.sizeof   // The size of the struct object
>     o.foo()    // The member of the struct object
>
> With classes, it operates sometimes on the reference and 
> sometimes on the referenced object:
>
>     o.sizeof   // The size of the class reference
>     o.foo()    // The member of the class object
>
> That's D's way of confusing on this topic.

 Only if o.sizeof may refer to the pointer size and not the class 
object.

> You meant sv:
>
>     return sv ? sv : 0;

 Ack! bad typo! Bad bad typo!

> That is still a compilation error for a statically-typed 
> language like D. The types of sv and 0 don't match. But the 
> line should always be like this anyway:
>
>     return sv ? *sv : 0;
>
> Because sv is always a pointer:

 Gotcha.

> > Rather than auto could ref work?
>
> You mean, 'ref' on the return type, right? For opIn_r, it 
> better not be 'ref', because then it would be returning a 
> reference to a local pointer:

 And here i thought it would refer the reference (from in's case) 
to the original object. Mmmm.

>     // Note ref return. I think this is a bug.
>     ref opIn_r(X)(X x)
>     {
>         foreach (hash; hashes)
>         {
>             auto p = x in hash;
>             if (p)
>                 return p; // <-- return a reference to the 
> local pointer
>         }
>
>         return null;
>     }
>
> I think that is a bug but the compiler does not give a warning 
> about returning a reference to the local pointer.

 I've actually had this problem before. Ended up having to use 
ref on the input and then force a pointer to get my code 
elsewhere to work (without copying). But that's kinda 
unimportant; If the compiler issues a warning than all the better 
now :)

> As you said, returning *x would return a copy of the value if 
> it were a struct.

 K. Glad to get that out of the way.
Top | Discussion index | About this forum | D home