digitalmars.D.learn - is it possible to have a default property for any given class ?
- someone (45/45) Jun 07 2021 Consider the following code:
- H. S. Teoh (42/93) Jun 07 2021 alias computers this;
- someone (34/74) Jun 07 2021 For the sake of clarity (to me) and, given I never liked code
- Jack (59/99) Jun 07 2021 I think you meant to implement ranges? you can implement in the
- someone (11/15) Jun 07 2021 Thanks for your reply !
Consider the following code: ```d class classComputer { private string pstrName; final property string name() { return this.pstrName; } final property void name(in string lstrName) { this.pstrName = lstrName; } this( string lstrComputerName ) { this.pstrName = lstrComputerName; } } class classComputers { classComputers lhs; classComputers rhs; int opApply(int delegate(classComputers) dg) { /// boilerplate code to handle the class's default collection if (lhs && lhs.opApply(dg)) return 1; if (dg(this)) return 1; if (rhs && rhs.opApply(dg)) return 1; return 0; } public classComputer[] computers; /// how can I tag this as the default property ? } void main ( ) { classComputers lobjComputers = new classComputers; lobjComputers.computers ~= new classComputer("dell"); lobjComputers.computers ~= new classComputer("ibm"); lobjComputers.computers ~= new classComputer("apple"); lobjComputers.computers[1].name = r"lenovo"; foreach(lobjComputer; lobjComputers.computers) { writeln(lobjComputer.name); } ///foreach(lobjComputer; lobjComputers) { writeln(lobjComputer.name); } /// with default property (if possible) } ``` The above code works correctly, however, avoiding the redundancy of lobjComputers.computers will be a plus. Also tell me if the collection is implemented the right way, it is my first code using the opApply() delegate which I don't deeply understand for the time being.
Jun 07 2021
On Mon, Jun 07, 2021 at 03:26:27PM +0000, someone via Digitalmars-d-learn wrote:Consider the following code: ```d class classComputer {[...]} class classComputers { classComputers lhs; classComputers rhs; int opApply(int delegate(classComputers) dg) { /// boilerplate code to handle the class's default collection if (lhs && lhs.opApply(dg)) return 1; if (dg(this)) return 1; if (rhs && rhs.opApply(dg)) return 1; return 0; } public classComputer[] computers; /// how can I tag this as the default property ?alias computers this;} void main ( ) { classComputers lobjComputers = new classComputers; lobjComputers.computers ~= new classComputer("dell"); lobjComputers.computers ~= new classComputer("ibm"); lobjComputers.computers ~= new classComputer("apple"); lobjComputers.computers[1].name = r"lenovo"; foreach(lobjComputer; lobjComputers.computers) { writeln(lobjComputer.name); } ///foreach(lobjComputer; lobjComputers) { writeln(lobjComputer.name); } /// with default property (if possible) } ``` The above code works correctly, however, avoiding the redundancy of lobjComputers.computers will be a plus. Also tell me if the collection is implemented the right way, it is my first code using the opApply() delegate which I don't deeply understand for the time being.It's very simple. Whenever some non-array object appears on the right side of a foreach() statement, the compiler looks for a method on the object called .opApply. If it exists, the loop body is passed to that method as a delegate. IOW: // This: foreach (item; myCollection) { /* loop body here */ } // Gets translated to this: myCollection.opApply((item) { /* loop body here */ }); Given that, your .opApply method doesn't really do what you want. It should instead be written like this: // N.B.: dg is NOT the type of the collection, but the // individual item you want to iterate over. int opApply(int delegate(classComputer) dg) { // Loop over the computers in the current node first foreach (computer; computers) { // Pass single item to loop body. auto ret = dg(computer); // N.B.: do NOT assume a non-zero return value // will always be 1; it may not be if your loop // body contains `break` or `continue`. if (ret) return ret; } // Now recurse child nodes if (lhs) { auto ret = lhs.opApply(dg); if (ret) return ret; // again, don't assume it will be 1 } if (rhs) { auto ret = rhs.opApply(dg); if (ret) return ret; // again, don't assume it will be 1 } return 0; } T -- Which is worse: ignorance or apathy? Who knows? Who cares? -- Erich Schubert
Jun 07 2021
On Monday, 7 June 2021 at 15:55:36 UTC, H. S. Teoh wrote: Thanks for your reply. It was very illustrating.It's very simple. Whenever some non-array object appears on the right side of a foreach() statement, the compiler looks for a method on the object called .opApply. If it exists, the loop body is passed to that method as a delegate. IOW: ```d // This: foreach (item; myCollection) { /* loop body here */ } // Gets translated to this: myCollection.opApply((item) { /* loop body here */ }); ``` Given that, your .opApply method doesn't really do what you want. It should instead be written like this: // N.B.: dg is NOT the type of the collection, but the // individual item you want to iterate over. ```d int opApply(int delegate(classComputer) dg) { // Loop over the computers in the current node first foreach (computer; computers) { // Pass single item to loop body. auto ret = dg(computer); // N.B.: do NOT assume a non-zero return value // will always be 1; it may not be if your loop // body contains `break` or `continue`. if (ret) return ret; } // Now recurse child nodes if (lhs) { auto ret = lhs.opApply(dg); if (ret) return ret; // again, don't assume it will be 1 } if (rhs) { auto ret = rhs.opApply(dg); if (ret) return ret; // again, don't assume it will be 1 } return 0; } ```For the sake of clarity (to me) and, given I never liked code with multiple returns, I rewrote your code (providing I am not introducing flaws) as following: ```d class classComputers { classComputers lhs; classComputers rhs; int opApply(int delegate(classComputer) dg) { /// boilerplate code to handle the class's default collection int lintResult = 0; foreach (lobjComputer; computers) { /// looping over the computers starting in current node lintResult = dg(lobjComputer); /// passing single object to the loop body if (lintResult != 0) { break; } } if (lintResult != 0 && lhs) { lintResult = lhs.opApply(dg); } /// recursing child nodes if (lintResult != 0 && rhs) { lintResult = rhs.opApply(dg); } /// recursing child nodes return lintResult; } public classComputer[] computers; alias computers this; /// ie: default property } ``` It seems it works perfectly well. Also, your: ```d alias computers this; ``` was very clever, indeed :)
Jun 07 2021
On Monday, 7 June 2021 at 15:26:27 UTC, someone wrote:Consider the following code: ```d class classComputer { private string pstrName; final property string name() { return this.pstrName; } final property void name(in string lstrName) { this.pstrName = lstrName; } this( string lstrComputerName ) { this.pstrName = lstrComputerName; } } class classComputers { classComputers lhs; classComputers rhs; int opApply(int delegate(classComputers) dg) { /// boilerplate code to handle the class's default collection if (lhs && lhs.opApply(dg)) return 1; if (dg(this)) return 1; if (rhs && rhs.opApply(dg)) return 1; return 0; } public classComputer[] computers; /// how can I tag this as the default property ? } void main ( ) { classComputers lobjComputers = new classComputers; lobjComputers.computers ~= new classComputer("dell"); lobjComputers.computers ~= new classComputer("ibm"); lobjComputers.computers ~= new classComputer("apple"); lobjComputers.computers[1].name = r"lenovo"; foreach(lobjComputer; lobjComputers.computers) { writeln(lobjComputer.name); } ///foreach(lobjComputer; lobjComputers) { writeln(lobjComputer.name); } /// with default property (if possible)I think you meant to implement ranges? you can implement in the way you wanted: ```foreach(lobjComputer; lobjComputers)``` but the recommend approach is to get this array through the array index operator. It would go like this: ```d class classComputer { private string pstrName; final string name() { return this.pstrName; } final void name(in string lstrName) { this.pstrName = lstrName; } this(string lstrComputerName = null) { this.pstrName = lstrComputerName; } classComputer lhs; classComputer rhs; int opApply(int delegate(classComputer) dg) { /// boilerplate code to handle the class's default collection if (lhs && lhs.opApply(dg)) return 1; if (dg(this)) return 1; if (rhs && rhs.opApply(dg)) return 1; return 0; } public classComputer[] computers; /// how can I tag this as the default property ? auto opIndex() nothrow { return Range(computers); } protected static struct Range { private classComputer[] a; auto front() { return a[0]; } auto back() { return a[$ - 1]; } void popFront() { a = a[1 .. $]; } bool empty() { return a.length == 0; } size_t opDollar() { return a.length; } auto opSlice(size_t start, size_t end) { return a[start .. end]; } } } void main () { import std.stdio : writeln; auto lobjComputers = new classComputer; lobjComputers.computers ~= new classComputer("dell"); lobjComputers.computers ~= new classComputer("ibm"); lobjComputers.computers ~= new classComputer("apple"); lobjComputers.computers[1].name = r"lenovo"; foreach(lobjComputer; lobjComputers[]) { writeln(lobjComputer.name); } ///foreach(lobjComputer; lobjComputers) { writeln(lobjComputer.name); } /// with default property (if possible) } ```} ```
Jun 07 2021
On Monday, 7 June 2021 at 16:10:08 UTC, Jack wrote:I think you meant to implement ranges? you can implement in the way you wanted: ```foreach(lobjComputer; lobjComputers)``` ...Thanks for your reply ! I am aware that ranges are one of D's gems, I did take an overview of them last night or the night before but did not get immersed in them too much. Will look at them. The first thing that came to mind was to implement them as a collection as I always did on other languages, but I suppose ranges could be an alternative.... but the recommend approach is to get this array through the array index operatorWhen you say so, you mean you prefer arrays over ranges or, that you disagree with the recommended approach for whatever reasons ? Could you elaborate ?
Jun 07 2021