December 16, 2013
On 12/16/13 1:45 PM, Walter Bright wrote:
> On 12/16/2013 12:38 PM, Andrei Alexandrescu wrote:
>> bool between(T, U1, U2)(T v, U1 lo, U2 hi)
>> {
>>      return v >= lo && v <= hi;
>> }
>
> You'd need 4 such functions, < <, < <=, <= <, <= <=, and it just seems
> like trivia.

The behavior is taken from SQL where it's closed both ends.

>> uint among(T, Us...)(T v, Us vals)
>> {
>>      foreach (i, U; Us)
>>      {
>>          if (v == vals[i]) return i + 1;
>>      }
>>      return 0;
>> }
>
> This has O(n) behavior, which might be unexpected for the user.

It's O(1) because the data size is in the source text. There's no variable n.

There's quite a bit of evidence in support of among (not as much for between) in the source code of Facebook's cpplint, soon to be open sourced. Here are some relevant quotes:

bool atBuiltinType(R)(R it) {
  return it.front.type_.among(tk!"double", tk!"float", tk!"int", tk!"short",
      tk!"unsigned", tk!"long", tk!"signed", tk!"void", tk!"bool", tk!"wchar_t",
      tk!"char") != 0;
}
...
string[] readQualifiedIdentifier(R)(ref R it) {
  string[] result;
  for (; it.front.type_.among(tk!"identifier", tk!"::"); it.popFront) {
    if (it.front.type_ == tk!"identifier") {
      result ~= it.front.value_;
    }
  }
  return result;
}
...
    if (it.front.type_.among(tk!"class", tk!"struct", tk!"union")) {
      result += callback(it, v);
    }
...
    if (it.front.type_.among(tk!"namespace", tk!"class", tk!"struct",
            tk!"union", tk!"{")) {
      auto term = it.find!(x => x.type_ == tk!"{");
      if (term.empty) {
        break;
      }
      it = skipBlock(term);
      continue;
    }
...
    if (v[i .. $].atSequence(tk!"#", tk!"if")
        || (v[i .. $].atSequence(tk!"#", tk!"identifier")
            && v[i + 1].value_.among("ifndef", "ifdef"))) {
      ++openIf;
    } else if (v[i .. $].atSequence(tk!"#", tk!"identifier")
        && v[i + 1].value_ == "endif") {
      ++i; // hop over the else
      --openIf;
    }
...
      auto term = it.find!((t) => t.type_.among(tk!":", tk!"{"));
...
static bool endsClass(CppLexer.TokenType2 tkt) {
  return tkt.among(tk!"\0", tk!"{", tk!";") != 0;
}
...
static bool isAccessSpecifier(CppLexer.TokenType2 tkt) {
  return tkt.among(tk!"private", tk!"public", tk!"protected") != 0;
}
...
      while (i.front.type_.among(tk!"*", tk!"const", tk!"volatile")) {
        i.popFront;
      }

I invite you all to contemplate redoing all these and more from first principles.


Andrei

December 16, 2013
On 12/16/13 1:47 PM, Brad Anderson wrote:
> On Monday, 16 December 2013 at 20:38:52 UTC, Andrei Alexandrescu wrote:
>> bool between(T, U1, U2)(T v, U1 lo, U2 hi)
>> {
>>     return v >= lo && v <= hi;
>> }
>>
>
> I must say that:
>
>      if (val.between(3, 10))
>
> sure is a lot easier to understand at a glance than:
>
>      if (val >= 3 && val <= 10)
>
> Although there is a problem with the word "between" not being clear
> about whether it is inclusive or not.

Prior art: SQL.


Andrei

December 16, 2013
On 2013-12-16 20:38:51 +0000, Andrei Alexandrescu said:

> bool between(T, U1, U2)(T v, U1 lo, U2 hi)
> {
>      return v >= lo && v <= hi;
> }
> 
> uint among(T, Us...)(T v, Us vals)
> {
>      foreach (i, U; Us)
>      {
>          if (v == vals[i]) return i + 1;
>      }
>      return 0;
> }
> 
> Add?
> 
> 
> Andrei

as Walter mentioned, between should have different versions. Aslo might be useful to have a generalized
Range type (not to be confused with iteration ranges). Having used Guava range [1] type, I can say it was very useful (especially implementing a predicate interface, enabling filtering with a range)

1. https://code.google.com/p/guava-libraries/wiki/RangesExplained

December 16, 2013
On Monday, 16 December 2013 at 22:10:28 UTC, Walter Bright wrote:
> Exactly, meaning I'd have to go look at the source code for it, whereas with the latter I can see right away what it is. The function is a net programmer time loss.

Surely you would turn to the documentation, not the source code. We could make it require the bounds template parameter, so it would always be required to use it like:

if(val.between!"[)"(0, 10))
   ...

However, with a good, solid default that is easy to remember, I wouldn't mind having a default argument for the bounds.

At any rate, the implementation of `among` should probably be something closer to:

---
uint among(T, Values...)(auto ref T value, auto ref Values values)
    if(!is(CommonType!(T, Values) == void))
{
    foreach(uint i, ref v; values)
    {
        if(value == v) return i + 1;
    }

    return 0;
}
---

It would of course be even better if we had a nice and clear way to express that all types in Values must be equatable (?) with T, something like `allSatisfy!(isComparable!("==", T), Values)`, where `isComparable` is the missing link.
December 16, 2013
On 12/16/2013 1:51 PM, Timon Gehr wrote:
> Do you mean O(vals.length)? O(vals.length)=O(1).

I can't argue with both you and Andrei :-)

I withdraw my complaint.
December 16, 2013
On 12/16/13 2:24 PM, Jakob Ovrum wrote:
> On Monday, 16 December 2013 at 22:10:28 UTC, Walter Bright wrote:
>> Exactly, meaning I'd have to go look at the source code for it,
>> whereas with the latter I can see right away what it is. The function
>> is a net programmer time loss.
>
> Surely you would turn to the documentation, not the source code. We
> could make it require the bounds template parameter, so it would always
> be required to use it like:
>
> if(val.between!"[)"(0, 10))
>     ...
>
> However, with a good, solid default that is easy to remember, I wouldn't
> mind having a default argument for the bounds.

I guess if we need several versions of between, we could give up on it.

> At any rate, the implementation of `among` should probably be something
> closer to:
>
> ---
> uint among(T, Values...)(auto ref T value, auto ref Values values)
>      if(!is(CommonType!(T, Values) == void))
> {
>      foreach(uint i, ref v; values)
>      {
>          if(value == v) return i + 1;
>      }
>
>      return 0;
> }
> ---
>
> It would of course be even better if we had a nice and clear way to
> express that all types in Values must be equatable (?) with T, something
> like `allSatisfy!(isComparable!("==", T), Values)`, where `isComparable`
> is the missing link.

A custom predicate defaulted to '==' would be the ticket.


Andrei

December 16, 2013
On Mon, Dec 16, 2013 at 01:45:38PM -0800, Walter Bright wrote:
> On 12/16/2013 12:38 PM, Andrei Alexandrescu wrote:
> >bool between(T, U1, U2)(T v, U1 lo, U2 hi)
> >{
> >     return v >= lo && v <= hi;
> >}
> 
> You'd need 4 such functions, < <, < <=, <= <, <= <=, and it just seems like trivia.
[...]

	bool between(string op1=">=", string op2="<=", T, U1, U2)
		    (T v, U1, lo, U2 hi)
	{
		return mixin("v" ~ op1 ~ "lo") &&
			mixin("v" ~ op2 ~ "hi");
	}

	x.between(y, z);	// y <= x <= z
	x.between!(">", "<")(y, z);	// y < x < z
	// etc.


T

-- 
"I'm not childish; I'm just in touch with the child within!" - RL
December 16, 2013
On 16/12/13 22:49, Timon Gehr wrote:
> There's the issue of different possibilities for inclusive/exclusive end points.
> Maybe we want to add a template parameter taking values from
> ["[)","()","(]","[]"]. Even then it is not too clear what the default should be.
> (Probably "[)".)

That would be in line with the way that it's handled in other Phobos cases where bounds can be open or closed at either end (e.g. std.random.uniform).
December 16, 2013
On 12/16/13, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> There's quite a bit of evidence in support of among (not as much for between) in the source code of Facebook's cpplint, soon to be open sourced. Here are some relevant quotes:

>From first glance it seems you're always using among when needing a
boolean result, so why does among have to return an index instead of a boolean true/false?
December 16, 2013
On 16/12/13 23:30, Andrei Alexandrescu wrote:
> I guess if we need several versions of between, we could give up on it.

What's wrong with having it implemented analogous to std.random.uniform -- taking a bounds parameter which allows for open and/or closed at either end, with the default being "[)" ... ?

By the way, I'd also like to see that open/closed-at-either-end specialization extended to std.range.iota().