oplossingen bij ambiguous function-sel.

Pagina: 1
Acties:
  • 139 views sinds 30-01-2008
  • Reageer

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Ik zit nu te goochelen met het narrowen van functies

vb:

void foo(int a, float b){}

void foo(float a, int b){}

foo(1,1)

Wat ga je hier zeggen? In java is het gebruikelijk om dan een ambigous error te geven omdat ze allebei even waardevol zijn.

Op dit moment ga ik gewoon van links naar rechts de argumenten bijlangs lopen, en dan steeds de beste te kiezen. Ik zou heb daarna ook nog een keer van rechts naar links kunnen laten lopen. Krijg ik daar een andere waarde voor terug, dan heb je een ambiguous call.

Hoe pakken andere talen dit aan? En wat is eigelijk wenselijk??

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
In de C# standaard komt dit uiteraard ook ter sprake:

http://www.jaggersoft.com/csharp_standard/14.4.2.htm .

Het wordt daar "overload resolution" genoemd, wat wel een aardige term is. In die gelinkte versie is het flink klikken voordat je wat zinnigs leest, dus misschien kan je beter even een PDF ophalen ;) . Deze specificatie is wel erg duidelijker.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
1 Given an argument list A with a set of argument types A1, A2, ..., AN and two applicable function members MP and MQ with parameter types P1, P2, ..., PN and Q1, Q2, ..., QN, MP is defined to be a better function member than MQ if

* 2 for each argument, the implicit conversion from AX to PX is not worse than the implicit conversion from AX to QX, and
* 3 for at least one argument, the conversion from AX to PX is better than the conversion from AX to QX.
Gelukkig heb ik niet vaak dit soort vage functie-verschillen bij mijn systemen. Ik laat hem denk ik voorlopig staan zoals ie nu is (van links naar rechts selecteren). Maar ik plaats er even een link bij naar dit topic.

En nu geestelijk voorbereiden voor de mutable types die ik morgen wil aanpakken..

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Alarmnummer schreef op 29 April 2003 @ 23:40:
Ik zit nu te goochelen met het narrowen van functies

vb:
void foo(int a, float b){}
void foo(float a, int b){}
foo(1,1)

Wat ga je hier zeggen? In java is het gebruikelijk om dan een ambigous error te geven omdat ze allebei even waardevol zijn.
Het enige juiste wat je kunt doen is volgens mij een error genereren dat er geen functie is die matcht met de aanroep. Immers '1' is een geheel getal, geen float. Een developer zal dus OF foo(1.0, 1) OF foo(1, 1.0) moeten doen. foo(1.0, 1.0) geeft dezelfde fout.
Op dit moment ga ik gewoon van links naar rechts de argumenten bijlangs lopen, en dan steeds de beste te kiezen. Ik zou heb daarna ook nog een keer van rechts naar links kunnen laten lopen. Krijg ik daar een andere waarde voor terug, dan heb je een ambiguous call.
Hoe pakken andere talen dit aan? En wat is eigelijk wenselijk??
Volgens mij krijg je in C++ gewoon een error tijdens de compile, want die herkent de '1' als een integer. Daar moet je bv 1.0f zeggen voor float en 1.0 voor double. Ook weer wat slap natuurlijk, maar het maakt het wel duidelijk als je de code leest en de compiler kan straight forward de code juist interpreteren en meteen de aanroep foo(int, int) tegen de bekende signatures aanhouden. Er volgt dan in jouw voorbeeld geen match, dus error op de regel foo(1,1).

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
EfBe schreef op 30 april 2003 @ 11:38:
[...]

Het enige juiste wat je kunt doen is volgens mij een error genereren dat er geen functie is die matcht met de aanroep. Immers '1' is een geheel getal, geen float. Een developer zal dus OF foo(1.0, 1) OF foo(1, 1.0) moeten doen. foo(1.0, 1.0) geeft dezelfde fout.
Ik geloof niet dat c(++ ) een fout meldt bij: sin(1) bv. Java geeft in ieder geval geen problemen.
Volgens mij krijg je in C++ gewoon een error tijdens de compile, want die herkent de '1' als een integer. Daar moet je bv 1.0f zeggen voor float en 1.0 voor double. Ook weer wat slap natuurlijk, maar het maakt het wel duidelijk als je de code leest en de compiler kan straight forward de code juist interpreteren en meteen de aanroep foo(int, int) tegen de bekende signatures aanhouden. Er volgt dan in jouw voorbeeld geen match, dus error op de regel foo(1,1).
Ik ben het idd met je eens dat het de duidelijkheid niet altijd ten goede komt.

Maar ik heb het nu zo in elkaar gezet dat een integer ook echt een subtype is van een float, dus iedere integer is ook element van de verzameling van alle waarden van float. En daarom zou je geen onnodige casts te hoeven doen (omdat iedere integerwaarde dus ook een floatwaarde is).

[ Voor 6% gewijzigd door Alarmnummer op 30-04-2003 11:52 ]


  • EfBe
  • Registratie: Januari 2000
  • Niet online
In dat geval (integer is subtype van float) heb je idd een probleem, want die upcast lukt altijd natuurlijk...

Ik weet overigens niet of het zo verstandig is om een integer een subtype van een float te maken. Ik snap wel dat je 10 gelijk kunt zien als 10.0 en dat dus in beide oplossingsdomeinen zit, maar waarom werk je dan niet louter met floats? Tenslotte kun je een int altijd upcasten naar een float, het is zn supertype tenslotte (dus een int meegeven aan een functie die een float verlangt).

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
EfBe schreef op 30 April 2003 @ 12:06:
In dat geval (integer is subtype van float) heb je idd een probleem, want die upcast lukt altijd natuurlijk...

Ik weet overigens niet of het zo verstandig is om een integer een subtype van een float te maken.
Het maakt het systeem een stuk logischer/eleganter. Ik hoef in de code verder nergens nog rekening te houden met special cases, en het systeem klopt ook qua logica. Dit systeem gaat dus niet gebruikt worden als programmeertaal voor een normale programmeur. Hiermee moet je rekening houden. Dit gaat gebruikt worden door 'knowledge engineers'. Mensen die de kennis van een expert gaan vastleggen in knowledgebases.
Ik snap wel dat je 10 gelijk kunt zien als 10.0 en dat dus in beide oplossingsdomeinen zit, maar waarom werk je dan niet louter met floats? Tenslotte kun je een int altijd upcasten naar een float, het is zn supertype tenslotte (dus een int meegeven aan een functie die een float verlangt).
Maar dan kan je niet meer aangeven dat je expliciet een integer wilt hebben. Bv voor een array index kan je natuurlijk niet met een gebroken getal komen aanzetten.

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Voor expliciet integer gebruik zou je impliciet die kunnen afronden naar een geheel getal. Of je inheritence tree omdraaien: een float is een subtype van een int (voegt fraction toe, ints hebben geen fraction).

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Alarmnummer: ik zie dat je met mutable types aan de slag gaat. Misschien is het dan interessant om eens dit stuk door te lezen: Multi-Level Algebraic Specification. Dit stuk bespreekt een soort formalisatie van een gelaagd systeem waar duidelijk wordt wat zulke zaken die 'boven' typen lijken te liggen nu eigenlijk zijn.

Het volgende recente paper over C# uitbreidingen houdt hiermee ook verband. Het bevat onder andere voorstellen voor non-null types, maar dan in een algemene aanpak (streams).

Unifying Tables, Objects and Documents

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • tomato
  • Registratie: November 1999
  • Niet online
Op mij komt het nogal krom over om integer als subtype te zien van float. Is dit niet juist net zo'n voorbeeld als oval en circle? Er zijn eigenschappen die float wel heeft maar integer niet, zoals de fraction.

Andersom zoals EfBe voorstelt is ook problematisch. Het integer type kan de garantie geven dat er altijd een geheel getal in zit (is ook een eigenschap) terwijl het float subtype dat dan niet zou kunnen.

[ Voor 4% gewijzigd door tomato op 01-05-2003 10:19 ]


  • EfBe
  • Registratie: Januari 2000
  • Niet online
tomato schreef op 01 May 2003 @ 10:18:
Andersom zoals EfBe voorstelt is ook problematisch. Het integer type kan de garantie geven dat er altijd een geheel getal in zit (is ook een eigenschap) terwijl het float subtype dat dan niet zou kunnen.
Je zou 'geheel getal' impliciet kunnen definieren, namelijk het is een geheel getal bij gebrek aan fraction. Als je Float van Integer derived, specialiseer je de Integer met een fraction, waardoor er gehele getallen (fraction == 0) als gebroken getallen (fraction != 0) in kunnen worden geplaatst. De 'garantie' zoals jij die definieert is dan een 'gevolg van' het ontbreken van die specialisatie, nl. er is geen fraction, waardoor je wel de eigenschap hebt dat er gehele getallen in staan, maar dat niet expliciet definieert.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

Alarmnummer schreef op 30 april 2003 @ 11:51:
Ik geloof niet dat c(++ ) een fout meldt bij: sin(1) bv. Java geeft in ieder geval geen problemen.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
void f (int, float) { }
void f (float, int) { }
void sin (float) { }
void cos (float) { }
void cos (double) { }

void b ()
{
    f (1, 1);
    sin (1);
    cos (1);
}

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"ComeauTest.c", line 9: error: more than one instance of overloaded function "f"
          matches the argument list, the choices that match are:
            function "f(int, float)"
            function "f(float, int)"
            The argument types that you used are: (int, int)
      f (1, 1);
      ^

"ComeauTest.c", line 11: error: more than one instance of overloaded function "cos"
          matches the argument list, the choices that match are:
            function "cos(float)"
            function "cos(double)"
            The argument types that you used are: (int)
      cos (1);
      ^
EfBe schreef op 30 april 2003 @ 14:43:
Voor expliciet integer gebruik zou je impliciet die kunnen afronden naar een geheel getal. Of je inheritence tree omdraaien: een float is een subtype van een int (voegt fraction toe, ints hebben geen fraction).
Dat klopt niet. Een int kun je impliciet casten naar een float. Dus een int is altijd een float. Andersom niet: een float kun je niet impliciet casten naar een int. Een float hoeft dus niet altijd een int te zijn (hoewel een int niet altijd in z'n geheel in een float past, maar dat wordt over het algemeen niet als probleem gezien)

Wiskundig is het precies hetzelfde
De verzameling R representeert alle reële getallen. Q is een subset daarvan, die alle relationele getallen representeerd. Daar is Z weer een subset van, dat ale gehele getallen (positief en negatief) representeert. En daar is N weer een subset van, waar alle positieve gehele getallen in zitten.

Dus een getal in N komt ook voor in R. Maar een getal in R hoeft niet per se in N voor te komen. Int is hier N, en float is hier R. Een int is dus altijd een float, maar een float hoeft geen int te zijn

de classtree zou er dan ook zo uitzien:
Java:
1
2
3
4
5
6
7
8
9
10
class Float
class Int extends Float;

void f1 (Float);
void f2 (Int);

f1 (1);   // ok, Int can impliciet gecast worden naar Float
f1 (1.0); // ok, Float is een Float
f2 (1);   // ok, Int is een Int
f2 (1.0); // error, een Float kan niet impliciet geupcast worden naar Int


Het gaat misschien wat tegen de principes in, omdat int geen uitbreiding is op float (eerder andersom idd, door er een fractational part aan toe te voegen). Maar int is wel een subset van float, en dus een subclass van float

[ Voor 17% gewijzigd door .oisyn op 01-05-2003 11:26 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
.oisyn: Int is hier N, en float is hier R.
Op zich begrijp ik de bedoeling, maar ik zou float niet gelijk willen stellen aan R ... Strikt genomen correspondeert de verzameling van mogelijke waarden van een Int natuurlijk ook niet met Z ;) (die verzameling is immers oneindig groot).

Dit geeft ook precies aan waarom imperatieve talen nog steeds geen goede indeling hebben van typen van getallen. Decimal in C# is al leuk, maar is nog niet echt alles. Je zou typen willen hebben als: complex, real, rational, float, integer.

Het ironische is dat Lisp al een fantastische indeling van getallen had, zoals veel daar eigenlijk al fantastisch was. Ook Haskell doet het buitengewoon aardig (de indeling is daar in feite weer op Lisp gebaseerd).

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • EfBe
  • Registratie: Januari 2000
  • Niet online
.oisyn schreef op 01 mei 2003 @ 11:18:
Dat klopt niet. Een int kun je impliciet casten naar een float. Dus een int is altijd een float. Andersom niet: een float kun je niet impliciet casten naar een int. Een float hoeft dus niet altijd een int te zijn (hoewel een int niet altijd in z'n geheel in een float past, maar dat wordt over het algemeen niet als probleem gezien)
Maar de float -> int conversie is in feite de upcast van float naar int in het model waar je float van int derived. Wat verlies van info oplevert, je verliest nl. de fraction, wat precies het resultaat is wat jij zegt: wanneer je een float (in het model van float derived van int) doorgeeft aan een functie die een int verwacht (base class van float) zal daar alleen de fraction-less part van de float (heeft ook een naam, kan er even niet opkomen) het gehele getal dus) beschikbaar zijn.

Als ik class Dier heb en ik derive daar de class Hond van, dan kan ik hond wel gebruiken als 'Dier' (het is tenslotte type 'Dier') maar je verliest de eigenschappen van de specifieke class Hond, precies wat je ook hebt wanneer je float van int derived.

Dmv implicit cast operators te definieren kun je de downcast bewerkstelligen tussen int en float, zoals dat vaak gedaan wordt. (half .NET zit er vol mee) de upcast van float naar int gaat al goed, je verliest gewoon de fraction.
Wiskundig is het precies hetzelfde
[..]
Dus een getal in N komt ook voor in R. Maar een getal in R hoeft niet per se in N voor te komen. Int is hier N, en float is hier R. Een int is dus altijd een float, maar een float hoeft geen int te zijn
Dat zou dan pleiten voor een class tree zoals Alarmnummer hem nu al heeft: float waar int vanaf derived. Echter, in class-trees gaat het erom dat je bij elke derive specialisatie toevoegt, eigenschappen die het type van het supertype specialiseren in het subtype. Qua eigenschappen bevat een 'float' alle eigenschappen van int en meer, nl. het definieert een fraction, vandaar dat het imho logisch is om float van int te deriven (mocht je uberhaupt daar een class tree van willen maken ;))
Het gaat misschien wat tegen de principes in, omdat int geen uitbreiding is op float (eerder andersom idd, door er een fractational part aan toe te voegen). Maar int is wel een subset van float, en dus een subclass van float
Gaat het bij OO modelling niet om definities van containers waar data in wordt geplaatst? Nu ga je de grenzen van de data die eventueel in de containers wordt geplaatst gebruiken om de class-hierarchie te modelleren, terwijl volgens mij de eigenschappen van de CONTAINERS moeten worden gebruikt voor het modellering van de classes, wat dus een omgekeerde tree tot gevolg heeft.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
.oisyn: Het gaat misschien wat tegen de principes in, omdat int geen uitbreiding is op float (eerder andersom idd, door er een fractational part aan toe te voegen). Maar int is wel een subset van float, en dus een subclass van float
Daarom wordt subclassing ook vaak los beschreven van inheritance. Jij vind het tegen de principes ingaan omdat een Int klasse die inherit van een Float geen uitbreidingen toevoegt. Qua subclassing is het echter prima. Sterker nog: in talen met object types staat subtyping weer los subclassing.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Wiskundig gezien is Z is een deelverzameling van Q, welke weer een deelverzameling is van R. Kort opgeschreven: N < Z < Q < R < C. Als je een functie hebt:

code:
1
Add :: C -> C -> C

Telt deze alle getallen in de verzamelingen N, Z, Q, R, C op. N is immers een subset van C, dus waarom zou je geen N kunnen meegeven als je een C eist?

De volgende functie:
code:
1
Add :: N -> N -> N

werkt op alle elementen van N. Dus niet op alle elementen van Z. Als je N een subklasse laat zijn van C (indirect) gaat dit goed: je kan een N meegeven waar je een C eist. Je kan aan een functie die een Z verwacht echter geen N meegeven.

Als je een Q een subklasse van een N maakt en je implementeert optelling in de klasse N, dan erft Q deze implementatie. Dit is echter niet logisch: optelling geimplementeerd voor N zal niet functioneren voor Q en alle implementaties moeten dus overridden worden.

Daarnaast is elke Q dus ook een N. Alle methoden die een N verlangen kunnen dus ook een Q voor hun kiezen krijgen. Je voorbeeld van een Hond en een Dier is typerend. Een Hond is namelijk een deelverzameling van alle dieren: elke Hond is een Dier. Is een Q echter een deelverzameling van N? Is elke Q een N?

Als je echte deelverzamelingen gebruikt als basis voor je klasse structuur kan je zoveel mogelijk code zo generiek mogelijk implementeren: zo hoog mogelijk in de klasse structuur. Als je dit gaat omdraaien, moet je de implementaties steeds gaan overriden omdat de klassen immers 'wat toevoegen'.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • EfBe
  • Registratie: Januari 2000
  • Niet online
"Zo hoog mogelijk zo generiek mogelijk implementeren". Volgens mij is dat iets wat niet echt lukt in de huidige talen zoals java, c# en C++, even los van generics gezien.

Wat ik me nu afvraag is: omdat dit 2 disjunctieve class trees oplevert, hoe je er naar kijkt (het deelverzamelingidee vs. de is-a idee van toevoegen van specialisatie), wat is nu correct? Want omdat ze disjunctief zijn kan er maar 1 correct zijn lijkt me.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
Volgens mij is het fundamentele begrip bij het ontwerpen van een klassenstructuur het abstraheren van een superklasse van zijn subklasses. Daarbij is de superklasse dus een algemener concept dan de specifieke subklassen die er onder vallen.

Een dier is duidelijk een generalisatie van een hond, net zoals een vervoermiddel een generalisatie van een auto is. In beide gevallen is de eerste dus de superklasse (dier, vervoermiddel) en de tweede de subklasse (hond, auto). Een vervoermiddel kun je (eventueel via een aantal omwegen) weer generaliseren tot een ding en een ding is weer een fysieke entiteit, en is daarmee alweer een meer concrete subklasse van een algemene superklasse als concept of iets dergelijks. Ik trek het principe van generalisatie nu dus tot in het extreme door, totdat ik uitkom bij een superklasse waar alles onder valt! Inderdaad is het zo dat ik vrijwel niets met een 'concept' kan; ik kan misschien vaststellen of twee 'concepten' gelijk zijn, maar veel meer valt er niet over te zeggen, zonder na te gaan of het een kleur of een vervoermiddel of een dier of een idee is.

Als iedereen het met me eens is dat ik hierboven een geldige klassehiërarchie heb opgebouwd, dan wordt ook duidelijk dat getallen precies hetzelfde zijn. Nu begin ik echter, ten behoeve van de discussie, bovenaan. Welke soort getallen bevat alle mogelijke meer specifieke soorten getallen? De antwoord op die vraag is een beetje afhankelijk van welke soort wiskunde je hanteert en op welk nivo. In een praktische situatie zou je waarschijnlijk reële getallen zeggen (ook integers zijn immers gehele getallen) of, een stapje hoger, complexe getallen (ook reële getallen en dus integers en floats zijn complexe getallen). Misschien wil je nog wel een stapje hoger gaan en ook irreële getallen meenemen, maar ik heb geen idee hoe zo'n verzameling getallen heet (en of daar ook complexe getallen onder zijn), maar dat is nu niet zo relevant.

Mijn punt is dat als je bovenaan begint met 'complexe getallen' met als subklasse 'reële getallen' en subklasse daarvan 'gehele getallen', je weer de meest algemene klasse (het meest abstracte concept) bovenaan hebt staan. De hiërarchie is dus precies hetzelfde opgebouwd! Weliswaar hoef je van een geheel getal veel minder op te slaan dan van een complex getal, maar conceptueel gezien voegt een geheel getal wel een concrete restrictie toe: het imaginaire deel en de fractie van het reële deel van een geheel getal zijn gelijk aan 0. Qua gedrag klopt het ook: methoden gedefinieerd voor complexe getallen werken ook voor gehele getallen (al is het resultaat dan ook complexe getal en niet altijd een geheel getal).

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
EfBe: "Zo hoog mogelijk zo generiek mogelijk implementeren". Volgens mij is dat iets wat niet echt lukt in de huidige talen zoals java, c# en C++, even los van generics gezien.
Dat valt toch wel mee denk ik. Ik denk dat je deze zin iets te serieus/zwaar naam ;) . Met generiek bedoel ik uiteraard slechts generiek voor een bepaalde verzameling klassen (dat is vaak de echte betekenis als men de term generiek gebruikt ;) ).

Vrijwel alles draait in deze talen om het zo generiek mogelijk implementeren van methoden. Elke methode die een klasse erft van een superklasse kan je zien als een generieke implementatie (voor alle subklassen dus). In een uitgebreide Collections library zie je goed hoe het zo hoog mogelijk implementeren van methoden zorgt voor enorm veel hergebruik in een klasse-structuur. Voorbeeld: Set en List implementaties zijn meestal subklassen van een abstracte Collection implementatie. Methoden als contains, isEmpty en size kan je zo hoog mogelijk, in een Collection, implementeren.

Geparameterizeerde typen staan daar helemaal los van. Ze creeeren geen nieuwe mogelijkheden, maar slechts meer compile time type checking.
(het deelverzamelingidee vs. de is-a idee van toevoegen van specialisatie), wat is nu correct? Want omdat ze disjunctief zijn kan er maar 1 correct zijn lijkt me.
Subclassing tbv het toevoegen van functionaliteit kan denk ik alleen onder de voorwaarde dat een instantie van die subklasse ook daadwerkelijk conceptueel behandeld kan worden als een instantie van de superklasse. Met andere woorden: de superklasse moet algemener, abstracter zijn dan de subklasse. Als de superklasse specifieker is (zoals Int tov Float) zit je in de problemen en ben je dus fout bezig.

Specialisatie zou dus dan en slechts dan toegepast moeten worden als de instanties van de gespecialiseerde klasse gezien kunnen worden als een subset van de superklasse. Voor Dier en Hond geldt dit, voor Person en Employee ook, voor Component en Button ook, voor List en Collection ook.

In een bepaald opzicht zou je het vereisen van een .0 deel ook als een stukje specialisatie kunnen zien.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Soultaker:Volgens mij is het fundamentele begrip bij het ontwerpen van een klassenstructuur het abstraheren van een superklasse van zijn subklasses. Daarbij is de superklasse dus een algemener concept dan de specifieke subklassen die er onder vallen.
Helemaal mee eens (ook met je goede uitleg :) )

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Dus is het deriven van float van int alleen 'goed' wanneer je kijkt naar de IMPLEMENTATIE van die 2 classes, dus dat je puur gericht bent op 'code', (want je bent dan code aan het toevoegen in de float class, die je niet hebt in de int class) terwijl je dus totaal niet focust op het concept (c) soultaker :). Float van int deriven is dan dus fout, het moet andersom zijn (of ze erven samen van een gemeenschappelijke superclass, bv 'valuetype') want het concept klopt anders niet, hetzelfde concept wat je juist dan dus wel hanteert om de derive 'Hond' van 'Dier' te maken. Ok duidelijk :)

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

.edit: misschien moet ik voortaan de rest van de topic doorlezen voordat ik reply :P
EfBe schreef op 01 May 2003 @ 11:49:
[...]

Maar de float -> int conversie is in feite de upcast van float naar int in het model waar je float van int derived. Wat verlies van info oplevert, je verliest nl. de fraction, wat precies het resultaat is wat jij zegt: wanneer je een float (in het model van float derived van int) doorgeeft aan een functie die een int verwacht (base class van float) zal daar alleen de fraction-less part van de float (heeft ook een naam, kan er even niet opkomen) het gehele getal dus) beschikbaar zijn.
Maar in diezelfde redenering kun je een functie die een Int verwacht ook een Float voeren, en dat is niet de bedoeling
Als ik class Dier heb en ik derive daar de class Hond van, dan kan ik hond wel gebruiken als 'Dier' (het is tenslotte type 'Dier') maar je verliest de eigenschappen van de specifieke class Hond, precies wat je ook hebt wanneer je float van int derived.
maar Hond is een subset van Dier. Net als int een subset van float is. Het is een beetje als het Circle <-> Oval probleem. Circle is dan imho een subclass van Oval, ookal heeft het minder eigenschappen. Eigenlijk heeft het juist restricties, maar die kun je weer niet opgeven in de meeste talen
Dmv implicit cast operators te definieren kun je de downcast bewerkstelligen tussen int en float, zoals dat vaak gedaan wordt. (half .NET zit er vol mee) de upcast van float naar int gaat al goed, je verliest gewoon de fraction.
ja maar het is de bedoeling dat je juist een error cq. warning krijgt als die (impliciete) upcast optreedt. Je hoort een functie die een int verwacht immers niet zomaar een float te kunnen geven
Dat zou dan pleiten voor een class tree zoals Alarmnummer hem nu al heeft: float waar int vanaf derived. Echter, in class-trees gaat het erom dat je bij elke derive specialisatie toevoegt, eigenschappen die het type van het supertype specialiseren in het subtype. Qua eigenschappen bevat een 'float' alle eigenschappen van int en meer, nl. het definieert een fraction, vandaar dat het imho logisch is om float van int te deriven (mocht je uberhaupt daar een class tree van willen maken ;))
Je kunt het ook zo zien: de specialisatie van int zorgt ervoor dat het fractational part altijd op 0 komt te staan. In plaats van data toe te voegen zorg je ervoor dat je het bereik van de data verkleind.
In principe zou je een klasse SubInt kunnen maken die alleen integers in het bereik van [0, 10] toe laat. Hier is geen uitbreiding van de data, maar een restrictie op de huidige data. Een functie dat een int accepteert kun je ook een SubInt voeren. Aan een functie dat altijd een int tussen 0 en 10 wilt kun je geen int geven.
Gaat het bij OO modelling niet om definities van containers waar data in wordt geplaatst? Nu ga je de grenzen van de data die eventueel in de containers wordt geplaatst gebruiken om de class-hierarchie te modelleren, terwijl volgens mij de eigenschappen van de CONTAINERS moeten worden gebruikt voor het modellering van de classes, wat dus een omgekeerde tree tot gevolg heeft.
imho is subclassing bij OO niet specifiek op het toevoegen van data gericht (inheritance). Bovendien zou de data private moeten zijn zodat het niet eens opvraagbaar is. Daar kun je wel weer functies voor maken, maar die kun je overriden in de subclasses.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface Float
{
    float getIntPart ();
    float getFractationalPart ();
}

class FloatImpl implements Float
{
    float f;
    float getIntPart () { return (int)f; }
    float getFloatPart () { return f - (int)f; }
}

interface Int extends Float { }

class IntImpl implements Int
{
    int i;
    float getIntPart () { return i; }
    float getFloatPart () { return 0; }
}

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1