www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - either

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
I wrote a simple helper, in spirit with some recent discussions:

// either
struct Either(Ts...)
{
     Tuple!Ts data_;
     bool opEquals(E)(E e)
     {
         foreach (i, T; Ts)
         {
             if (data_[i] == e) return true;
         }
         return false;
     }
}

auto either(Ts...)(Ts args)
{
     return Either!Ts(tuple(args));
}

unittest
{
     assert(1 == either(1, 2, 3));
     assert(4 != either(1, 2, 3));
     assert("abac" != either("aasd", "s"));
     assert("abac" == either("aasd", "abac", "s"));
}

Turns out this is very useful in a variety of algorithms. I just don't 
know where in std this helper belongs! Any ideas?


Andrei
Jan 09 2011
next sibling parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench robertmuench.de> writes:
On 2011-01-09 19:42:22 +0100, Andrei Alexandrescu said:

 I wrote a simple helper, in spirit with some recent discussions:

Hi, EITHER normaly means: This or that, so it's more like an IF/ELSE. How about ANY? IMO 1==any(1, 2, 3) describes pretty good what is meant. -- Robert M. Münch http://www.robertmuench.de
Jan 09 2011
next sibling parent reply Daniel Gibson <metalcaedes gmail.com> writes:
Am 09.01.2011 19:48, schrieb Robert M. Münch:
 On 2011-01-09 19:42:22 +0100, Andrei Alexandrescu said:

 I wrote a simple helper, in spirit with some recent discussions:

Hi, EITHER normaly means: This or that, so it's more like an IF/ELSE. How about ANY? IMO 1==any(1, 2, 3) describes pretty good what is meant.

I agree.
Jan 09 2011
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/9/11 1:12 PM, Daniel Gibson wrote:
 Am 09.01.2011 19:48, schrieb Robert M. Münch:
 On 2011-01-09 19:42:22 +0100, Andrei Alexandrescu said:

 I wrote a simple helper, in spirit with some recent discussions:

Hi, EITHER normaly means: This or that, so it's more like an IF/ELSE. How about ANY? IMO 1==any(1, 2, 3) describes pretty good what is meant.

I agree.

Problem with any/either is that it encodes the comparison inside the data. You can do == and !=, but algorithms want to work with general predicates, and any can't work with arbitrary predicates - unless the predicates themselves are designed to work with it. For now I added this to std.algorithm: /** Consume all elements from $(D r) that are equal to one of the elements $(D es). */ void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es) { loop: for (; !r.empty; r.popFront()) { foreach (i, E; Es) { if (binaryFun!pred(r.front, es[i])) { continue loop; } } break; } } unittest { auto s1 = "Hello world"; skipAll(s1, 'H', 'e'); assert(s1 == "llo world"); } I wonder if it's worthwhile encoding the predicate inside the compared elements. In that case, any would return a type that also has the predicate. Something like this: struct Any(alias pred = "a == b", Es...) { ... } Then you'd put the predicate by the call to any, not by the call to the algorithm: skipAll(r, any!"a > b"(x, y, z)); Then skipAll doesn't need to worry about the predicate - it just uses == and !=. If this paradigm has a landslide advantage over the current structure, we'd need to rewrite a lot of std.algorithm. Andrei
Jan 09 2011
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 I think "any" is used in Python IIRC, so I'll have to go +1 with Robert here.

any() and all() in Python have a different purpose, the first is true if one or more than the given items is true, and the second is true if they all true:
 all([1, 2, 4])



 all(["", None, 0])



 all(["", None, 1])



 any(["", None, 1])



 any([1, 2, 4])



 any(["", None, 0])



Bye, bearophile
Jan 09 2011
prev sibling next sibling parent Mafi <mafi example.org> writes:
Am 09.01.2011 19:42, schrieb Andrei Alexandrescu:
 I wrote a simple helper, in spirit with some recent discussions:

 // either
 struct Either(Ts...)
 {
 Tuple!Ts data_;
 bool opEquals(E)(E e)
 {
 foreach (i, T; Ts)
 {
 if (data_[i] == e) return true;
 }
 return false;
 }
 }

 auto either(Ts...)(Ts args)
 {
 return Either!Ts(tuple(args));
 }

 unittest
 {
 assert(1 == either(1, 2, 3));
 assert(4 != either(1, 2, 3));
 assert("abac" != either("aasd", "s"));
 assert("abac" == either("aasd", "abac", "s"));
 }

 Turns out this is very useful in a variety of algorithms. I just don't
 know where in std this helper belongs! Any ideas?


 Andrei

think is a better name. There also 'none', 'all' and 'one'. There seems to be strange bug with the opEquals. Everything works but if you replace the opEquals template with it's actual instantiation dmd cry at you. See http://www.digitalmars.com/d/2.0/operatoroverloading.html#equals and my last post.
Jan 09 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I think "any" is used in Python IIRC, so I'll have to go +1 with Robert here.
Jan 09 2011
prev sibling next sibling parent reply Tomek =?ISO-8859-2?Q?Sowi=F1ski?= <just ask.me> writes:
Andrei Alexandrescu napisa=B3:

 I wrote a simple helper, in spirit with some recent discussions:
=20
 // either
 struct Either(Ts...)
 {
      Tuple!Ts data_;
      bool opEquals(E)(E e)
      {
          foreach (i, T; Ts)
          {
              if (data_[i] =3D=3D e) return true;
          }
          return false;
      }
 }
=20
 auto either(Ts...)(Ts args)
 {
      return Either!Ts(tuple(args));
 }
=20
 unittest
 {
      assert(1 =3D=3D either(1, 2, 3));
      assert(4 !=3D either(1, 2, 3));
      assert("abac" !=3D either("aasd", "s"));
      assert("abac" =3D=3D either("aasd", "abac", "s"));
 }

I really don't dig the whole helper structs with overloaded operators thing= . It complicates the implementation (more work for compiler to grok and inl= ine) and you're never really sure what it does unless you read the docs (or= the complicated implementation). It should be as simple as this: bool any(E, Ts...)(E e, Ts args) { foreach (a; args) if (a =3D=3D e) return true; return false; } unittest { assert(!"abac".any("aasd", "s")); assert("abac".any("aasd", "abac", "s")); // assert(1.any(1,2,3)); // doesn't compile for now, bug 3382 }
 Turns out this is very useful in a variety of algorithms.

Very!
 I just don't=20
 know where in std this helper belongs! Any ideas?

Maybe std.algorithm? It bears vague resemblance to max(). --=20 Tomek
Jan 09 2011
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/9/11 2:09 PM, Tomek Sowiñski wrote:
 I really don't dig the whole helper structs with overloaded operators thing.
It complicates the implementation (more work for compiler to grok and inline)
and you're never really sure what it does unless you read the docs (or the
complicated implementation).

 It should be as simple as this:

 bool any(E, Ts...)(E e, Ts args) {
       foreach (a; args)
           if (a == e)
              return true;
       return false;
 }

 unittest
 {
       assert(!"abac".any("aasd", "s"));
       assert("abac".any("aasd", "abac", "s"));
       // assert(1.any(1,2,3));    // doesn't compile for now, bug 3382
 }

 Turns out this is very useful in a variety of algorithms.

Very!
 I just don't
 know where in std this helper belongs! Any ideas?

Maybe std.algorithm? It bears vague resemblance to max().

Aha, so this encodes the predicate in the operation. With a general predicate, that would be: if (any!"a != b"(expr, 1, 2, 5)) { ... } The advantage over if (expr != 1 || expr != 2 || expr != 5)) { ... } is terseness and the guarantee that expr is evaluated once (which is nice at least for my code). This looks promising and well integrated with the rest of Phobos. Should I add it? And if I do, is "any" the name? Andrei
Jan 09 2011
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/9/11 3:13 PM, Tomek Sowiñski wrote:
 Andrei Alexandrescu napisa³:

 Aha, so this encodes the predicate in the operation. With a general
 predicate, that would be:

 if (any!"a != b"(expr, 1, 2, 5)) { ... }

 The advantage over

 if (expr != 1 || expr != 2 || expr != 5)) { ... }

 is terseness and the guarantee that expr is evaluated once (which is
 nice at least for my code).

Yes.
 This looks promising and well integrated with the rest of Phobos. Should
 I add it?

Please do. Oh, thinking about it 10 more minutes, an improvement struck me: bool any(alias pred = "a==b", E, Ts...)(E e, Ts args) if(Ts.length> 1 || !isTuple!Ts) { foreach (a; args) if (binaryFun!pred(a, e)) return true; return false; } unittest { assert(!"abac".any("s")); assert(!"abac".any("aasd", "s")); assert("abac".any("aasd", "abac", "s")); } /// Unpacks a single tuple. bool any(alias pred = "a==b", E, T)(E e, T t) if(isTuple!T) { return any!(pred, E, T.Types)(e, t.field); } unittest { assert(any("abac", tuple("aasd", "abac", "s"))); assert(find!any([5,3,7,9,4,5,7], tuple(2,4,6)) == [4,5,7]); // cool! }

Hm, I don't see why not asking the owner of the tuple to type .expand.
 And if I do, is "any" the name?

For a start. We can always vote it to sth more descriptive e.g. 'isAny' in case 'any' turns out misleading.

At best we'd minimize such jitter. Andrei
Jan 09 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 09 January 2011 12:28:23 Andrei Alexandrescu wrote:
 On 1/9/11 2:09 PM, Tomek Sowi=F1ski wrote:
 I really don't dig the whole helper structs with overloaded operators
 thing. It complicates the implementation (more work for compiler to grok
 and inline) and you're never really sure what it does unless you read
 the docs (or the complicated implementation).
=20
 It should be as simple as this:
=20
 bool any(E, Ts...)(E e, Ts args) {
=20
       foreach (a; args)
      =20
           if (a =3D=3D e)
          =20
              return true;
      =20
       return false;
=20
 }
=20
 unittest
 {
=20
       assert(!"abac".any("aasd", "s"));
       assert("abac".any("aasd", "abac", "s"));
       // assert(1.any(1,2,3));    // doesn't compile for now, bug 3382
=20
 }
=20
 Turns out this is very useful in a variety of algorithms.

Very! =20
 I just don't
 know where in std this helper belongs! Any ideas?

Maybe std.algorithm? It bears vague resemblance to max().

Aha, so this encodes the predicate in the operation. With a general predicate, that would be: =20 if (any!"a !=3D b"(expr, 1, 2, 5)) { ... } =20 The advantage over =20 if (expr !=3D 1 || expr !=3D 2 || expr !=3D 5)) { ... } =20 is terseness and the guarantee that expr is evaluated once (which is nice at least for my code). =20 This looks promising and well integrated with the rest of Phobos. Should I add it? And if I do, is "any" the name?

Well, I would expect any to be used for testing whether a predicate is true= for=20 any element in a given range (as discussed in=20 http://d.puremagic.com/issues/show_bug.cgi?id=3D4405 ), which is similar to= what=20 canFind() already does (though I'd very much like generalized any() and all= ()=20 functions as discussed in that enhancement request). Here, you are trying to do something similar, but in essence, you're giving= it=20 the needle first and then variadic lists to make up the haystack. It makes = me=20 want to try and combine what you're trying to do with any()/canFind(), but = given=20 that that would entail flipping the predicate, I'm not sure that that would= work=20 very well. Actually, I'd wonder if it would make sense to try and give an overload of= =20 std.algorithm.equal() which does what you're trying to do here. Either that= or=20 make it equalAny(), since it's using a predicate for equality in the same s= ense=20 that equal() is. Regardless, I'd quite like any() to be used for checking whether a predicat= e is=20 true for any element in a given range (with all() being used to check wheth= er=20 the predicate is true for all of the elements of a range), and using any() = for=20 what you're suggesting goes against that, even though it's quite similar. =2D Jonathan M Davis
Jan 09 2011
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:igcvll$29k5$1 digitalmars.com...
I wrote a simple helper, in spirit with some recent discussions:

 // either
 struct Either(Ts...)
 {
     Tuple!Ts data_;
     bool opEquals(E)(E e)
     {
         foreach (i, T; Ts)
         {
             if (data_[i] == e) return true;
         }
         return false;
     }
 }

 auto either(Ts...)(Ts args)
 {
     return Either!Ts(tuple(args));
 }

 unittest
 {
     assert(1 == either(1, 2, 3));
     assert(4 != either(1, 2, 3));
     assert("abac" != either("aasd", "s"));
     assert("abac" == either("aasd", "abac", "s"));
 }

 Turns out this is very useful in a variety of algorithms. I just don't 
 know where in std this helper belongs! Any ideas?

For years I've just been doing this: if( [1, 2, 3].contains(1) ) Although I think I needed to write a new "contains" to wrap existing functions when I switched from Tango to Phobos. Of course, "1 in [1,2,3]" would be much better ;) But whateever. Either way. But I suppose your "either" avoids an allocation, doesn't it? (And I'd lean more towards "any" than "either" like the other people).
Jan 09 2011
parent "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:igd57p$2k1k$1 digitalmars.com...
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:igcvll$29k5$1 digitalmars.com...
I wrote a simple helper, in spirit with some recent discussions:

 // either
 struct Either(Ts...)
 {
     Tuple!Ts data_;
     bool opEquals(E)(E e)
     {
         foreach (i, T; Ts)
         {
             if (data_[i] == e) return true;
         }
         return false;
     }
 }

 auto either(Ts...)(Ts args)
 {
     return Either!Ts(tuple(args));
 }

 unittest
 {
     assert(1 == either(1, 2, 3));
     assert(4 != either(1, 2, 3));
     assert("abac" != either("aasd", "s"));
     assert("abac" == either("aasd", "abac", "s"));
 }

 Turns out this is very useful in a variety of algorithms. I just don't 
 know where in std this helper belongs! Any ideas?

For years I've just been doing this: if( [1, 2, 3].contains(1) ) Although I think I needed to write a new "contains" to wrap existing functions when I switched from Tango to Phobos. Of course, "1 in [1,2,3]" would be much better ;) But whateever. Either way. But I suppose your "either" avoids an allocation, doesn't it? (And I'd lean more towards "any" than "either" like the other people).

It'd be really cool to have that work for the other comparison operators, too.
Jan 09 2011
prev sibling next sibling parent Tomek =?ISO-8859-2?Q?Sowi=F1ski?= <just ask.me> writes:
Andrei Alexandrescu napisa=B3:

 Aha, so this encodes the predicate in the operation. With a general=20
 predicate, that would be:
=20
 if (any!"a !=3D b"(expr, 1, 2, 5)) { ... }
=20
 The advantage over
=20
 if (expr !=3D 1 || expr !=3D 2 || expr !=3D 5)) { ... }
=20
 is terseness and the guarantee that expr is evaluated once (which is=20
 nice at least for my code).

Yes.
 This looks promising and well integrated with the rest of Phobos. Should=

 I add it?

Please do. Oh, thinking about it 10 more minutes, an improvement struck me: bool any(alias pred =3D "a=3D=3Db", E, Ts...)(E e, Ts args) if(Ts.length > = 1 || !isTuple!Ts) { foreach (a; args) if (binaryFun!pred(a, e)) return true; return false; } unittest { assert(!"abac".any("s")); assert(!"abac".any("aasd", "s")); assert("abac".any("aasd", "abac", "s")); } /// Unpacks a single tuple. bool any(alias pred =3D "a=3D=3Db", E, T)(E e, T t) if(isTuple!T) { return any!(pred, E, T.Types)(e, t.field); } unittest { assert(any("abac", tuple("aasd", "abac", "s"))); assert(find!any([5,3,7,9,4,5,7], tuple(2,4,6)) =3D=3D [4,5,7]); // = cool! }
 And if I do, is "any" the name?

For a start. We can always vote it to sth more descriptive e.g. 'isAny' in = case 'any' turns out misleading. --=20 Tomek
Jan 09 2011
prev sibling next sibling parent Tomek =?ISO-8859-2?Q?Sowi=F1ski?= <just ask.me> writes:
Andrei Alexandrescu napisa=B3:

 bool any(alias pred =3D "a=3D=3Db", E, Ts...)(E e, Ts args) if(Ts.lengt=


       foreach (a; args)
           if (binaryFun!pred(a, e))
              return true;
       return false;
 }

 unittest
 {
       assert(!"abac".any("s"));
       assert(!"abac".any("aasd", "s"));
       assert("abac".any("aasd", "abac", "s"));
 }

 /// Unpacks a single tuple.
 bool any(alias pred =3D "a=3D=3Db", E, T)(E e, T t) if(isTuple!T) {
      return any!(pred, E, T.Types)(e, t.field);
 }

 unittest
 {
       assert(any("abac", tuple("aasd", "abac", "s")));
       assert(find!any([5,3,7,9,4,5,7], tuple(2,4,6)) =3D=3D [4,5,7]);  =


 } =20

Hm, I don't see why not asking the owner of the tuple to type .expand.

I'd ask the reverse question -- why "abc".any(tuple("cba", "abc")) shouldn'= t mean "abc".any("cba", "abc") ? It's natural. The only difficulty I see is behaving well for the degenerate case ("abc".a= ny("abc") works, so tuple(1,2).any(tuple(1,2)) should work too). That, I be= lieve, can be solved by additional template constraints on the unpacking ov= erload. Another good reason -- this doesn't compile: assert(find!any([5,3,7,9,4,5,7], tuple(2,4,6).expand) =3D=3D [4,5,7]); test.d(24): Error: template std.algorithm.find(alias pred =3D "a =3D=3D b",= R,E) if (isInputRange!(R) && is(typeof(binaryFun!(pred)(haystack.front,need= le)) : bool)) does not match any function template declaration test.d(24): Error: template std.algorithm.find(alias pred =3D "a =3D=3D b",= R,E) if (isInputRange!(R) && is(typeof(binaryFun!(pred)(haystack.front,need= le)) : bool)) cannot deduce template function from argument types !(any)(in= t[],int,int,int) test.d(24): Error: template instance errors instantiating template --=20 Tomek
Jan 09 2011
prev sibling parent reply Justin Johansson <noreply jj.com> writes:
On 10/01/11 05:42, Andrei Alexandrescu wrote:
 I wrote a simple helper, in spirit with some recent discussions:

 // either
 struct Either(Ts...)
 {
 Tuple!Ts data_;
 bool opEquals(E)(E e)
 {
 foreach (i, T; Ts)
 {
 if (data_[i] == e) return true;
 }
 return false;
 }
 }

 auto either(Ts...)(Ts args)
 {
 return Either!Ts(tuple(args));
 }

 unittest
 {
 assert(1 == either(1, 2, 3));
 assert(4 != either(1, 2, 3));
 assert("abac" != either("aasd", "s"));
 assert("abac" == either("aasd", "abac", "s"));
 }

 Turns out this is very useful in a variety of algorithms. I just don't
 know where in std this helper belongs! Any ideas?

Despite that it may be very useful as you say, personally I think it is a fundamental no-no to overload the meaning of "==" in any manner that does not preserve the generally accepted semantics of equality which include the notions of reflexivity, symmetry and transitivity**. **See http://en.wikipedia.org/wiki/Equality_%28mathematics%29 The symmetric and transitive properties of the equality relation imply that if (a == c) is true and if (b == c) is true then (a == b) is also true. In this case the semantics of the overloaded "==" operator have the expressions 1 == either(1, 2, 3) and 2 == either(1, 2, 3) both evaluating to true and by implication/expectation (1 == 2). Clearly though, (1 == 2) evaluates to false in terms of the commonly accepted meaning of equality. Just my 2 cents and I wonder if there some other way of achieving the desired functionality of your helper without resorting to overloading "==" and the consequential violation of the commonly held semantics of equality. Cheers, Justin Johansson
Jan 11 2011
next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
On Jan 11, 11 17:10, Justin Johansson wrote:
 On 10/01/11 05:42, Andrei Alexandrescu wrote:
 I wrote a simple helper, in spirit with some recent discussions:

 // either
 struct Either(Ts...)
 {
 Tuple!Ts data_;
 bool opEquals(E)(E e)
 {
 foreach (i, T; Ts)
 {
 if (data_[i] == e) return true;
 }
 return false;
 }
 }

 auto either(Ts...)(Ts args)
 {
 return Either!Ts(tuple(args));
 }

 unittest
 {
 assert(1 == either(1, 2, 3));
 assert(4 != either(1, 2, 3));
 assert("abac" != either("aasd", "s"));
 assert("abac" == either("aasd", "abac", "s"));
 }

 Turns out this is very useful in a variety of algorithms. I just don't
 know where in std this helper belongs! Any ideas?

Despite that it may be very useful as you say, personally I think it is a fundamental no-no to overload the meaning of "==" in any manner that does not preserve the generally accepted semantics of equality which include the notions of reflexivity, symmetry and transitivity**. **See http://en.wikipedia.org/wiki/Equality_%28mathematics%29 The symmetric and transitive properties of the equality relation imply that if (a == c) is true and if (b == c) is true then (a == b) is also true. In this case the semantics of the overloaded "==" operator have the expressions 1 == either(1, 2, 3) and 2 == either(1, 2, 3) both evaluating to true and by implication/expectation (1 == 2). Clearly though, (1 == 2) evaluates to false in terms of the commonly accepted meaning of equality. Just my 2 cents and I wonder if there some other way of achieving the desired functionality of your helper without resorting to overloading "==" and the consequential violation of the commonly held semantics of equality. Cheers, Justin Johansson

We could use in instead of == if (1 in oneOf(1, 2, 3)) { ... } if (4 !in oneOf(1, 2, 3)) { ... }
Jan 11 2011
parent Justin Johansson <noreply jj.com> writes:
On 12/01/11 06:28, KennyTM~ wrote:
 On Jan 11, 11 17:10, Justin Johansson wrote:
 On 10/01/11 05:42, Andrei Alexandrescu wrote:
 I wrote a simple helper, in spirit with some recent discussions:
 unittest
 {
 assert(1 == either(1, 2, 3));
 assert(4 != either(1, 2, 3));
 assert("abac" != either("aasd", "s"));
 assert("abac" == either("aasd", "abac", "s"));
 }

desired functionality of your helper without resorting to overloading "==" and the consequential violation of the commonly held semantics of equality.

if (1 in oneOf(1, 2, 3)) { ... } if (4 !in oneOf(1, 2, 3)) { ... }

Nice suggestion. At the end of the day though it basically boils down to having either a binary operator** or a function for it. (** preferably excluding "==" and other undesirable operator overloads of course).
Jan 11 2011
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I don't really know what I'm up to, but I ended up making this thing:

https://gist.github.com/776604

I can't figure out how to make two nice templates called anySatisfy
and allSatisfy and make them call find and compare the lengths. So I
just ripped these two from std.typetuple and made them operate on
variadic arguments. Well, at least it's somewhat amusing to look at..
Jan 12 2011