www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - This needs a different approach

reply "Saaa" <empty needmail.com> writes:
I have this piece of code:

enum { APPLE, PEAR .. PLUM}

switch (data.type)
  {
   case APPLE:
   groupModule.appleModule.eat();
    break;
   case PEAR:
   groupModule.pearModule.eat();
    break;
..
..
   case PLUM:
   groupModule.plumModule.eat();
    break;
   default:
    break;
  }

As the function is always the same I'd rather see a lookup iso an iteration 
over all options.. but how?

groupModule.(data.type).eat();

Something like this is both faster and a lot less to code :) 
Apr 28 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Saaa" <empty needmail.com> wrote in message 
news:fv4q7a$1nde$1 digitalmars.com...
 I have this piece of code:

 enum { APPLE, PEAR .. PLUM}

 switch (data.type)
  {
   case APPLE:
   groupModule.appleModule.eat();
    break;
   case PEAR:
   groupModule.pearModule.eat();
    break;
 ..
 ..
   case PLUM:
   groupModule.plumModule.eat();
    break;
   default:
    break;
  }

 As the function is always the same I'd rather see a lookup iso an 
 iteration over all options.. but how?

 groupModule.(data.type).eat();

 Something like this is both faster and a lot less to code :)
Not sure if this works, but what you probably want is a function table: auto functionArray = [APPLE:&groupModule.appleModule.eat, PEAR:&groupModule.pearModule.eat, .., PLUM:&groupModule.plumModule.eat]; functionArray[data.type](); Another option is to store your modules in the groupModule in an array and just get a common interface returned via a function get: groupModule.get(data.type).eat(); -Steve
Apr 28 2008
parent reply "Saaa" <empty needmail.com> writes:
 I have this piece of code:

 enum { APPLE, PEAR .. PLUM}

 switch (data.type)
  {
   case APPLE:
   groupModule.appleModule.eat();
    break;
   case PEAR:
   groupModule.pearModule.eat();
    break;
 ..
 ..
   case PLUM:
   groupModule.plumModule.eat();
    break;
   default:
    break;
  }

 As the function is always the same I'd rather see a lookup iso an 
 iteration over all options.. but how?

 groupModule.(data.type).eat();

 Something like this is both faster and a lot less to code :)
Not sure if this works, but what you probably want is a function table: auto functionArray = [APPLE:&groupModule.appleModule.eat, PEAR:&groupModule.pearModule.eat, .., PLUM:&groupModule.plumModule.eat]; functionArray[data.type]();
Ok, that works; I think :) although the array would become very large.
 Another option is to store your modules in the groupModule in an array and 
 just get a common interface returned via a function get:
Erm, how does one do this?
 groupModule.get(data.type).eat();

 -Steve

 
Apr 28 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Saaa" wrote
 I have this piece of code:

 enum { APPLE, PEAR .. PLUM}

 switch (data.type)
  {
   case APPLE:
   groupModule.appleModule.eat();
    break;
   case PEAR:
   groupModule.pearModule.eat();
    break;
 ..
 ..
   case PLUM:
   groupModule.plumModule.eat();
    break;
   default:
    break;
  }

 As the function is always the same I'd rather see a lookup iso an 
 iteration over all options.. but how?

 groupModule.(data.type).eat();

 Something like this is both faster and a lot less to code :)
Not sure if this works, but what you probably want is a function table: auto functionArray = [APPLE:&groupModule.appleModule.eat, PEAR:&groupModule.pearModule.eat, .., PLUM:&groupModule.plumModule.eat]; functionArray[data.type]();
Ok, that works; I think :) although the array would become very large.
Not any larger than the code that is generated from your switch statement :) In fact, compilers sometimes optimize code like yours into a function array.
 Another option is to store your modules in the groupModule in an array 
 and just get a common interface returned via a function get:
Erm, how does one do this?
I was assuming your groupModule was a class, but now I think it might be a module (duh!). You should seriously consider putting these things into classes instead of modules, and looking up the right interface based on the data type from a function. For example: interface Edible { void eat(); } class Pear : Edible { void eat() {...} } static Edible[] fruits; static this() { fruits = [APPLE:new Apple(), PEAR:new Pear()...].dup; } Edible get(uint type) { if(type > fruits.length) return null; return fruits[type]; } Robert is right, this is precisely the thing that OO programming was created for :) -Steve
Apr 28 2008
parent reply "Saaa" <empty needmail.com> writes:
 I was assuming your groupModule was a class, but now I think it might be a 
 module (duh!).
; )
 You should seriously consider putting these things into classes instead of 
 modules, and looking up the right interface based on the data type from a 
 function.  For example:

 interface Edible
 {
   void eat();
 }

 class Pear : Edible
 {
   void eat() {...}
 }

 static Edible[] fruits;

 static this()
 {
    fruits = [APPLE:new Apple(), PEAR:new Pear()...].dup;
 }

 Edible get(uint type)
 {
    if(type > fruits.length)
       return null;
    return fruits[type];
 }

 Robert is right, this is precisely the thing that OO programming was 
 created for :)

 -Steve
I should have written this before, but data is actually an array. enum { APPLE, PEAR .. PLUM} for(i=0;i<1000;i++) { switch (data[i].type) { case APPLE: groupModule.appleModule.eat(); break; case PEAR: groupModule.pearModule.eat(); break; .. .. case PLUM: groupModule.plumModule.eat(); break; default: break; } }
Apr 28 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Saaa" wrote
 You should seriously consider putting these things into classes instead 
 of modules, and looking up the right interface based on the data type 
 from a function.  For example:

 interface Edible
 {
   void eat();
 }

 class Pear : Edible
 {
   void eat() {...}
 }

 static Edible[] fruits;

 static this()
 {
    fruits = [APPLE:new Apple(), PEAR:new Pear()...].dup;
 }

 Edible get(uint type)
 {
    if(type > fruits.length)
       return null;
    return fruits[type];
 }

 Robert is right, this is precisely the thing that OO programming was 
 created for :)

 -Steve
I should have written this before, but data is actually an array. enum { APPLE, PEAR .. PLUM} for(i=0;i<1000;i++) { switch (data[i].type) { case APPLE: groupModule.appleModule.eat(); break; case PEAR: groupModule.pearModule.eat(); break; .. .. case PLUM: groupModule.plumModule.eat(); break; default: break; } }
So with the get method I outlined above, the switch code becomes: get(data[i].type).eat(); -Steve
Apr 28 2008
parent reply "Saaa" <empty needmail.com> writes:
I think I get your code now :)

Tell me if I'm wrong:
This approach makes initializing the  fruits list a bit more complex.
Changing an item in the list to another fruit requires the GC (the previous 
instance needs to be deleted)
Changing an item is more costly than changing a number. (A lot of the items 
get changed 30 times per second)

Wouldn't a function table be more appropriate?

 So with the get method I outlined above, the switch code becomes:

 get(data[i].type).eat();

 -Steve
 
Apr 28 2008
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Premature optimization is bad.

Try doing it that way, and only optimize it if you see it's working slowly.

Saaa escribió:
 I think I get your code now :)
 
 Tell me if I'm wrong:
 This approach makes initializing the  fruits list a bit more complex.
 Changing an item in the list to another fruit requires the GC (the previous 
 instance needs to be deleted)
 Changing an item is more costly than changing a number. (A lot of the items 
 get changed 30 times per second)
 
 Wouldn't a function table be more appropriate?
 
 So with the get method I outlined above, the switch code becomes:

 get(data[i].type).eat();

 -Steve
Apr 28 2008
parent reply "Saaa" <empty needmail.com> writes:
 Premature optimization is bad.

 Try doing it that way, and only optimize it if you see it's working 
 slowly.
What is bad about function tables? Implementing function tables should be easy, I think :) At least a lot less work than rewriting everything into classes. I'm not optimizing, I'm simply deciding which approach is best. Why would using function tables be an optimization?
Apr 28 2008
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Saaa escribió:
 Premature optimization is bad.

 Try doing it that way, and only optimize it if you see it's working 
 slowly.
What is bad about function tables? Implementing function tables should be easy, I think :) At least a lot less work than rewriting everything into classes. I'm not optimizing, I'm simply deciding which approach is best. Why would using function tables be an optimization?
I just mentioned optimization because you said "Changing an item is more costly than changing a number. (A lot of the items get changed 30 times per second)". I'm more into solving this using classes and inheritance. If the compiler can do the function table for you, why bother?
Apr 28 2008
parent "Saaa" <empty needmail.com> writes:
 I'm more into solving this using classes and inheritance. If the compiler 
 can do the function table for you, why bother?
I can understand your position ;) But I didn't use any classes. If the compiler would optimize the switch to a function table anyways I would just keep it like it is now although learning to use classes in D sounds intriguing as well.
Apr 29 2008
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Saaa" wrote
I think I get your code now :)

 Tell me if I'm wrong:
 This approach makes initializing the  fruits list a bit more complex.
 Changing an item in the list to another fruit requires the GC (the 
 previous instance needs to be deleted)
 Changing an item is more costly than changing a number. (A lot of the 
 items get changed 30 times per second)

 Wouldn't a function table be more appropriate?
Maybe :) You keep changing the rules, so I'm not sure now. From your original code it appears you are calling functions from modules, I don't see how those functions are going to change during execution of the code. I'm really confused by your example, but in general, using OO principals, you are essentially hiding the specific implementation behind a generic interface. The generic interface is something that is Edible. The specifics of what happens when you call it on an instance of Edible should be irrelavent to the calling code. It just knows that something needs to be eaten, it doesn't care how that is done :) Imagine later on down the road you need to add another fruit. This is trivial, just add it to the initialized array, and every instance that calls get() doesn't care that get() can now return another fruit type. If you had your switch statement, then you have to update it in the calling code. You could even dynamically map values to fruits. All this, and the caller of get() doesn't have to care what fruits there are, and how they are used. It just knows that whatever is returned from get() has an eat() function which it needs to call. Separating interface from implementation is not only useful, but is more understandable, maintainable, and generally contains less bugs. That's not to say you have to use OO principals to solve every problem, but a problem in which you are calling a similar function on lots of similar objects (or modules) screams to be solved with OO :) You should find a good book on design patterns, they are immensely useful and mostly universal (though usually geared towards Java). I'm sure there is one that solves this. -Steve
Apr 28 2008
parent "Saaa" <empty needmail.com> writes:
 Wouldn't a function table be more appropriate?
Maybe :) You keep changing the rules, so I'm not sure now.
Yeah, sorry about that. (Stricly speaking it more like elaboriting than changin ;)
 From your original code it appears you are calling functions from modules, 
 I don't see how those functions are going to change during execution of 
 the code.
I didn't mean to say this, I meant to say that fruits may change into other fruits. (although not as often as I mentioned)
I'm really confused by your example, but in general, using OO principals, 
you are essentially hiding the specific implementation behind a generic 
interface.  The generic interface is something that is Edible.  The 
specifics of what happens when you call it on an instance of Edible should 
be irrelavent to the calling code.  It just knows that something needs to 
be eaten, it doesn't care how that is done :)  Imagine later on down the 
road you need to add another fruit.  This is trivial, just add it to the 
initialized array, and every instance that calls get() doesn't care that 
get() can now return another fruit type.  If you had your switch statement, 
then you have to update it in the calling code.  You could even dynamically 
map values to fruits.  All this, and the caller of get() doesn't have to 
care what fruits there are, and how they are used.  It just knows that 
whatever is returned from get() has an eat() function which it needs to 
call.

 Separating interface from implementation is not only useful, but is more 
 understandable, maintainable, and generally contains less bugs.  That's 
 not to say you have to use OO principals to solve every problem, but a 
 problem in which you are calling a similar function on lots of similar 
 objects (or modules) screams to be solved with OO :)  You should find a 
 good book on design patterns, they are immensely useful and mostly 
 universal (though usually geared towards Java).  I'm sure there is one 
 that solves this.

 -Steve
Using the switch with a function table wouldn't be more error-prone as how difficult can it be to find that I left out a fruit from the function table or switch. As for the restriction of the interface on all the fruits; couldn't this be done on modules? forcing every module to implement an eat function? (just asking :) I can understand the need of abstraction if it weren't a one-man show. But to me it seems like more work, most of the time. I really appreciate your code as it helps me understand oop, but to me it is not more understandable than the function table: also understanding function tables to memory usage is straight forward. Maybe it is my lack of understanding oop but most of the time I can't translate it to memory usage that easily (which is the point of oop, I think?). With the arsenal of D functionality, wouldn't it be possible to let the compiler generate the function table code?
Apr 29 2008
prev sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
Saaa schrieb:

 Tell me if I'm wrong:
 This approach makes initializing the  fruits list a bit more complex.
 Changing an item in the list to another fruit requires the GC (the previous 
 instance needs to be deleted)
 Changing an item is more costly than changing a number. (A lot of the items 
 get changed 30 times per second)
describe the program flow - but without code does the fruit realy change? - why?
Apr 28 2008
parent reply "Saaa" <empty needmail.com> writes:
 Tell me if I'm wrong:
 This approach makes initializing the  fruits list a bit more complex.
 Changing an item in the list to another fruit requires the GC (the 
 previous instance needs to be deleted)
 Changing an item is more costly than changing a number. (A lot of the 
 items get changed 30 times per second)
describe the program flow - but without code does the fruit realy change? - why?
I confused two arrays yesterday. The fruits don't change as often as I said (more like once every few seconds for only a small percentage of all fruits). As to how the flow is; that loop is called 30 times / sec. And eating a fruit may change it to another fruit.
Apr 29 2008
parent reply dennis luehring <dl.soluz gmx.net> writes:
 I confused two arrays yesterday. The fruits don't change as often as I said 
 (more like once every few seconds for only a small percentage of all 
 fruits).
ok that sounds better ... until now
 As to how the flow is; that loop is called 30 times / sec. And eating a 
 fruit may change it to another fruit.
and why does eating a fruit change it to another? is that the way your current algorithm work - or why does fruits change if eaten? more story details, please...
Apr 29 2008
parent reply "Saaa" <empty needmail.com> writes:
 I confused two arrays yesterday. The fruits don't change as often as I 
 said (more like once every few seconds for only a small percentage of all 
 fruits).
ok that sounds better ... until now
 As to how the flow is; that loop is called 30 times / sec. And eating a 
 fruit may change it to another fruit.
and why does eating a fruit change it to another? is that the way your current algorithm work - or why does fruits change if eaten? more story details, please...
Does this really matter? In oop style this might sound a bit strange, but in my setup it is the same as changing the type number. A different type means a different way of being eaten.
Apr 29 2008
parent dennis luehring <dl.soluz gmx.net> writes:
 and why does eating a fruit change it to another?
 is that the way your current algorithm work - or why does fruits change if 
 eaten? more story details, please...
 Does this really matter?
 In oop style this might sound a bit strange, but in my setup it is the same 
 as changing the type number.
desgins in oop become strange if the idea of what your program should do is an procedural codesnippet of how it should work :-)
 A different type means a different way of being eaten.
thats fully ok - but why does an eaten fruit morph into another one? sometimes people want it all in just one array maybe you need another one
Apr 29 2008
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Saaa wrote:
 I have this piece of code:
 
 enum { APPLE, PEAR .. PLUM}
 
 switch (data.type)
   {
    case APPLE:
    groupModule.appleModule.eat();
     break;
    case PEAR:
    groupModule.pearModule.eat();
     break;
 ..
 ..
    case PLUM:
    groupModule.plumModule.eat();
     break;
    default:
     break;
   }
 
 As the function is always the same I'd rather see a lookup iso an iteration 
 over all options.. but how?
 
 groupModule.(data.type).eat();
 
 Something like this is both faster and a lot less to code :) 
Isn't this what inheritance and polymorphism is all about?
Apr 28 2008
parent reply "Saaa" <empty needmail.com> writes:
"Robert Fraser" <fraserofthenight gmail.com> wrote in message 
news:fv4t21$1ue1$1 digitalmars.com...
 Saaa wrote:
 I have this piece of code:

 enum { APPLE, PEAR .. PLUM}

 switch (data.type)
   {
    case APPLE:
    groupModule.appleModule.eat();
     break;
    case PEAR:
    groupModule.pearModule.eat();
     break;
 ..
 ..
    case PLUM:
    groupModule.plumModule.eat();
     break;
    default:
     break;
   }

 As the function is always the same I'd rather see a lookup iso an 
 iteration over all options.. but how?

 groupModule.(data.type).eat();

 Something like this is both faster and a lot less to code :)
Isn't this what inheritance and polymorphism is all about?
Could you elaborate on that? The eat functions are totally different btw.
Apr 28 2008
next sibling parent reply Jesse Phillips <jessekphillips gmail.com> writes:
On Mon, 28 Apr 2008 19:22:15 +0200, Saaa wrote:

 "Robert Fraser" <fraserofthenight gmail.com> wrote in message
 news:fv4t21$1ue1$1 digitalmars.com...
 Saaa wrote:
 I have this piece of code:

 enum { APPLE, PEAR .. PLUM}

 switch (data.type)
   {
    case APPLE:
    groupModule.appleModule.eat();
     break;
    case PEAR:
    groupModule.pearModule.eat();
     break;
 ..
 ..
    case PLUM:
    groupModule.plumModule.eat();
     break;
    default:
     break;
   }

 As the function is always the same I'd rather see a lookup iso an
 iteration over all options.. but how?

 groupModule.(data.type).eat();

 Something like this is both faster and a lot less to code :)
Isn't this what inheritance and polymorphism is all about?
Could you elaborate on that? The eat functions are totally different btw.
Say you have a Fruit interface with the function eat(); You could then have an Apple, Plum, Pear class that implements Fruit. If you have data as one of these types maybe in a variable of type Fruit you could then just call data.eat(). Though you don't seem to have it set up in this way, and my not be the needed approach for what you are trying to do. I'm tired so I'm not going in as much depth as I should.
Apr 28 2008
parent "Saaa" <empty needmail.com> writes:
 Say you have a Fruit interface with the function eat();
 You could then have an Apple, Plum, Pear class that implements Fruit. If
 you have data as one of these types maybe in a variable of type Fruit you
 could then just call data.eat(). Though you don't seem to have it set up
 in this way, and my not be the needed approach for what you are trying to
 do. I'm tired so I'm not going in as much depth as I should.
Thanks, I'll look into it.
Apr 28 2008
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Saaa wrote:
 Could you elaborate on that?
 The eat functions are totally different btw. 
Here's how I would solve it: interface IFruit { void eat(); } IFruit APPLE; IFruit PEAR; //... IFruit PLUM; static this() { APPLE = new class() IFruit { void eat() { tasteTheJuicyGoodness(); } } // ... } You could initialize them in different modules, if you wanted, too.
Apr 28 2008
parent reply "Saaa" <empty needmail.com> writes:
 Here's how I would solve it:

 interface IFruit
 {
     void eat();
 }
I understand the interface, its a bit like a (partial) set of rules a class needs to implement
 IFruit APPLE;
 IFruit PEAR;
 //...
 IFruit PLUM;
Different instances of the interface.
 static this()
I use this on a module level, it gets run before main. Is it the same here?
 {
     APPLE = new class() IFruit
     {
         void eat()
         {
             tasteTheJuicyGoodness();
: D
         }
     }

     // ...
 }
How would you make the array of fruits? Something I can iterate over. like: fruits[ APPLE,PLUM,APPLE ... PEAR,PLUM];
 You could initialize them in different modules, if you wanted, too.
I want that : D
Apr 29 2008
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Saaa" <empty needmail.com> wrote in message 
news:fv7ht6$2d6c$1 digitalmars.com...

 How would you make the array of fruits?
 Something I can iterate over. like:
 fruits[ APPLE,PLUM,APPLE ... PEAR,PLUM];
IFruit[] fruits = [APPLE, PLUM, APPLE ... PEAR, PLUM]; Since all fruits inherit from IFruit, they can all be represented as IFruit references. So, if you have an array of IFruit, you can put all different kinds of fruits in it.
Apr 29 2008
parent reply "Saaa" <empty needmail.com> writes:
 IFruit[] fruits = [APPLE, PLUM, APPLE ... PEAR, PLUM];

 Since all fruits inherit from IFruit, they can all be represented as 
 IFruit references.  So, if you have an array of IFruit, you can put all 
 different kinds of fruits in it.
Calling 'fruit[2].eat();' would call eat() from that specific APPLE, right?
Apr 29 2008
parent reply "Saaa" <empty needmail.com> writes:
 IFruit[] fruits = [APPLE, PLUM, APPLE ... PEAR, PLUM];

 Since all fruits inherit from IFruit, they can all be represented as 
 IFruit references.  So, if you have an array of IFruit, you can put all 
 different kinds of fruits in it.
Calling 'fruit[2].eat();' would call eat() from that specific APPLE, right?
Changing fruit[2] to PLUM would go how? I would like the PLUM to retain data from that specific APPLE. That data is of the same type over all fruits.
Apr 29 2008
parent reply Gide Nwawudu <gide btinternet.com> writes:
On Tue, 29 Apr 2008 23:38:52 +0200, "Saaa" <empty needmail.com> wrote:

 IFruit[] fruits = [APPLE, PLUM, APPLE ... PEAR, PLUM];

 Since all fruits inherit from IFruit, they can all be represented as 
 IFruit references.  So, if you have an array of IFruit, you can put all 
 different kinds of fruits in it.
Calling 'fruit[2].eat();' would call eat() from that specific APPLE, right?
Changing fruit[2] to PLUM would go how?
Just assign as normal, fruits[2] = PLUM. [CODE] import std.stdio; interface IFruit { void eat(); } IFruit APPLE; IFruit PEAR; IFruit PLUM; static this() { APPLE = new class() IFruit { void eat() { writefln("Eat APPLE"); } }; PEAR = new class() IFruit { void eat() { writefln("Eat PEAR"); } }; PLUM = new class() IFruit { void eat() { writefln("Eat PLUM"); } }; } int main(string[] args) { IFruit[] fruits = [APPLE, PEAR, APPLE, APPLE, PEAR, PLUM]; writefln("Fruits"); foreach( f; fruits) { f.eat(); } writefln("\nNew Fruits"); fruits[2] = PLUM; foreach( f; fruits) { f.eat(); } return 0; } [/CODE] Gide
Apr 30 2008
parent "Saaa" <empty needmail.com> writes:
 IFruit[] fruits = [APPLE, PLUM, APPLE ... PEAR, PLUM];

 Since all fruits inherit from IFruit, they can all be represented as
 IFruit references.  So, if you have an array of IFruit, you can put all
 different kinds of fruits in it.
Calling 'fruit[2].eat();' would call eat() from that specific APPLE, right?
Changing fruit[2] to PLUM would go how?
Just assign as normal, fruits[2] = PLUM. [CODE] import std.stdio; interface IFruit { void eat(); } IFruit APPLE; IFruit PEAR; IFruit PLUM; static this() { APPLE = new class() IFruit { void eat() { writefln("Eat APPLE"); } }; PEAR = new class() IFruit { void eat() { writefln("Eat PEAR"); } }; PLUM = new class() IFruit { void eat() { writefln("Eat PLUM"); } }; } int main(string[] args) { IFruit[] fruits = [APPLE, PEAR, APPLE, APPLE, PEAR, PLUM]; writefln("Fruits"); foreach( f; fruits) { f.eat(); } writefln("\nNew Fruits"); fruits[2] = PLUM; foreach( f; fruits) { f.eat(); } return 0; } [/CODE] Gide
Thanks Gide (and everybody else), I think I will implement it alike your code. Should be interesting enough so expect more questions in the future :)
Apr 30 2008