people[name=="Andrew"].friends ~= peter
Oskar Linde
oskar.lindeREM at OVEgmail.com
Fri May 5 08:56:29 PDT 2006
Hi Antonio,
antonio wrote:
> As I introduced in http://www.dsource.org/forums/viewtopic.php?t=967
> object relations could be seen as hierarchycal structures.
>
> ¿Why not to introduce native syntax to "navigate into"/"Selec from" this
> kind of hierarchies?
Interesting idea. This looks like a general short hand way of expressing
select and map operations. The syntax has some problems though. See the
comments below.
In my std.array proposal
http://www.digitalmars.com/d/archives/digitalmars/D/35455.html I have
implemented .filter() and .map() function templates that allow a way of
expressing the below examples in a way that work with current D. (Maybe
.filter() should be renamed .select()?).
I am attaching examples of how your examples would look with my
std.array syntax. Those examples are rather wordy, mostly because there
is no short hand notation for declaring single expression delegates. I'm
not saying my examples show a better way to do things than your
suggestion (quite the opposite), but they show how those kinds of
expressions can be written today. My method also has the downside of
creating and then iterating over temporary arrays. It would be great if
one could find a way to define iterable array views. I will probably
look into that soon.
[After rereading, I'm not sure it was a good idea to include this:]
As another perspective, I've played with the thought that D had a way of
expressing single expression delegates and show how the code would look
then. This hypothetical syntax is:
{|T x| x+5}
which is equivalent to:
delegate auto(T x) { return x+5; }
where auto is typeof(x+5).
...
> ex 1: Add Peter to the friends of people named Andrew and older than 18.
>
> Person peter = ...;
> Person[] people = ...;
>
> people[name=="Andrew" && age>18].friends ~= peter;
With the suggested std.array, the following examples work:
foreach (p; people.filter(delegate bool(Person p) {
return p.name == "Andrew" && p.age > 18;
}))
p.friends ~= peter;
people.filter(delegate bool(Person p) {
return p.name == "Andrew" && p.age > 18;
})
.map(delegate void(Person p) { p.friends ~= peter; });
(I'm not sure about the best way to use whitespace in such code...)
And a hypothetical example with a short hand delegate notation:
people.filter( {|Person p| p.name == "Andrew" && p.age > 18} )
.map( {|Person p| p.friends ~= peter} );
>
> ex 2: Obtain the array of not married people childs.
> ex 2.a: with duplicates:
>
> Person[] modernChilds = people[!married].childs;
I don't fully understand the semantics of this one. I assume
Person.childs is of type Person[]. In that case, the return type of the
above would logically be Person[][], not Person[]. In that case:
Person[][] modernChilds = people
.filter(delegate bool(Person p) { return !p.married; })
.map(delegate Person[](Person p) { return p.childs; });
With the (IMHO, strange) assumption that the return value should be a
concatenated array, just add a .join().
Hypothetical:
Person[][] modernChilds = people
.filter( {|Person p| !p.married } )
.map( {|Person p| p.childs } );
> ex 2.b: without duplicates:
>
> Person[] modernChilds = Distinct(people[!married].childs);
Thanks for the idea. .distinct() (or maybe .unique()) is something I
definitely should add to the std.array proposal. :)
> ex 3: Do something with the married childs of people with friends named
> Andrew
> ex 3.a: using foreach (1 by 1 evaluation)
>
> foreach( Person someone;
> people[friends[name=="Andrew"].length!=0].childs[married] ){
> someone.doSomething();
> }
foreach(someone; people
.filter(delegate bool(Person p) {
return p.friends.find(delegate bool(Person p) {
return p.name == "Andrew";
}) != -1;
})
.map(delegate Person[](Person p) { return p.childs; })
.join()
.filter(delegate Person[](Person p){ return p.married; })) {
someone.doSomething();
}
(Phew)
Or, assuming Person has get-methods, the last map, join, filter, could
be something like:
.map(&Person.getChilds).join().filter(&Person.isMarried))
>
> ex 3.b: Only 1 stament:
>
> people[friends[name=="Andrew"].length!=0].childs[married].doSomething();
Same as above, but with a .map instead of foreach. (Maybe a void version
of .map() should be named .each() instead...?)
Hypothetical:
people
.filter({|Person p| p.friends.contains({|Person p| p.name=="Andrew"})})
.map( {|Person p| p.childs } )
.join()
.filter( {|Person p| p.married } )
.map( {|Person p| p.doSomething() } );
>
> ex 3.c: without duplicates
> Distinct(people[friends[name=="Andrew"].length!=0].childs[married].doSomething();
>
>
> (¿could Distinct be solved with a Template?)
Yes. Something like this (could of course be made more efficient):
import /*std.*/array;
// Double dereference link above for implementation.
// Used for .find() and the MakeDynamic template.
/** Returns an array containing only one occurrence of each element.
The resulting elements are sorted in order of first occurrence.
*/
template unique(ArrTy) {
MakeDynamic!(ArrTy) unique(ArrTy arr) {
MakeDynamic!(ArrTy) ret;
foreach(uint ix, e; arr) {
if (arr[0..ix].find(e) == -1)
ret ~= e;
}
return ret;
}
}
import std.stdio;
void main() {
writefln("%s","ababcdbdcbdbaba".unique());
}
// Prints "abcd"
(MakeDynamic is just there to support static arrays, such as char[15]).
> ---
>
> These examples could be solved using "D" standard syntax.
>
> ex: (thanks to csauls)
>
> Person[] p = cities[name=="Madrid"].population[age>18 && name=="Peter"];
>
> is equivalent to
>
> Person[] p;
> foreach (City x; cities) {
> with (x) {
> if (name == "Madrid") {
> foreach (Person y; population) {
> with (y) {
> if (age > 18 && name == "Peter")
> p ~= y;
> }
> }
> }
> }
> }
>
> Basically:
>
> ARRAY[CONDITION].SOMETHING;
>
> could be traslated to:
>
> foreach(T x; ARRAY)
> with(x)
> if( CONDITION ) {
> SOMETHING;
> }
>
> When CONDITION contains sub-ARRAY evaluations, it could be expanded as
>
> foreach(T x; ARRAY)
> with(x) {
> bool b;
> """Expand CONDITION and put result into b""";
> if( b ) {
> DOSOMETHING;
> }
> }
>
> Of course, this "expanding" method doesn't solve all the posibilities...
>
> people[age>10] = new Person("Foo");
>
> ---
>
> The main discussion about this idea was focused in 2 points:
> 1. "dot" or "not dot":
> people[.married && .age>18]
> vs
> people[married && age>18]
>
> "not dot" is more "D" syntax compliat (thanks to csaul).
This has the same problem as arr[length].
Assume you have the following piece of code:
void petersBirthday() {
Person people[] = everyone();
int index = people.indexOf(peter);
people[index].age++;
}
What happens if Person contains a member called index?
> 2.Array syntax vs "Template" syntax:
> people[married && age>18]
> vs
> people![married && age>18]
>
> Personally, I prefer "Array syntax":
> Person p = people[5];
> Person[] p = people[3..5]; // 3..5 is a "condition"
> Person[] p = people[5]; // ¿why not?
> Person[] p = people[married];
> Person[] p = people[age>18 && married];
>
The array syntax seems ambiguous. I think normal (single element)
indexing needs to have a different syntax from selection/filtered
indexing. (Unless D had a more stringent separation of bool vs numeric
variables.)
You also suggest that T should be implicitly convertible to T[] of
length 1. Is that a good idea?
How would this work with user defined containers?
Best regards,
Oskar
More information about the Digitalmars-d
mailing list