[Design OO / Java] Ontwerp van wiskundige expressies

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

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

Topicstarter
(overleden)
Aanleiding
Nadat ik enkele hoofdstukken Haskell had doorgebladerd, begon ik me toch af te vragen of dit makkelijke opbouwen van wiskundige expressies niet ongeveer gelijk ingeplementeerd kon worden in Java of any OO taal.

Wat wil ik nu
Ik wil een wiskundige functie kunnen voorstellen zo mooi als dat ook in functionele programmeer talen kan. Hiervoor is misschien wat uitleg nodig

Stel ik heb de volgende wiskundige expressie:
code:
1
sin( x^2 + 7x + cos( 30x ) )


Deze expressie is opgebouwt uit een willekeurig aantal expressies. Om dit goed voor je te zien heb ik de expressie daarom opgebouwt dmv een boom zoals ik hem zie

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        sin
                    |
                | ===   +       
            |
                | ===   +
            |   |
            |   | === x^2
            |   |
            |   | === 7x
            |                       
            |
                | ===   cos
                        |
                        | === 30x


Deze stuctuur wil ik ook makkelijk kunnen weergeven in Java en dan zo flexibel dat ik elk moment op elke willekeurige plek expressies kan vervangen / verwijderen / toevoegen

Wat heb ik nu
Ik heb mijn verse GoF boek er maar eens op nageslagen en kwam tot de conclusie dat 'Composite' het beste de structuur weergaf die ik wou gaan toepassen. Daarna ben ik aan het designen gegaan en daar kwam het volgende uit:
Afbeeldingslocatie: http://oege.ie.hva.nl/~schie14/designMath.gif

Hierin is MathExpression de algemene interface waarmee ik de expressie wil samenvoegen,
Java:
1
2
3
4
5
6
7
8
9
10
11
12
public interface MathExpression
{
        /**
          * Needed for implementation of the 'Composite'
          * design pattern.
          */
        public void add( MathExpression expression );
        public void remove( MathExpression expression );
        public Iterator getChildren( );
        
        public double calculate( double variable );
}


AbstractMathExpression een abstracte klasse die het standaard gedrag voor het patroon implementeert ( administratie van de childs ). Hieronder even snel wat code
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public abstract class AbstractMathExpression
{
        /**
          * Needed for implementation of the 'Composite'
          * design pattern.
          */
        public void add( MathExpression expression ) {
        
                getExpressions( ).add( expression );
        }
                        
        public void remove( MathExpression expression ) {
        
                // removal of the expression from _expressions
                //
        }
        
        public Iterator getChildren( ) {
           
                return getExpressions( ).iterator( );
        }
        
        public abstract double calculate( double variable );
        
        /**
          * getters and setters
          */
        private ArrayList getExpressions( ) {
        
                return _expressions;
        }
        
        private void setExpressions( ArrayList expressions ) {
        
                //Imagine nullcheck here
                //
                _expressions = expressions;
        }
        
        private ArrayList _expressions;
        
}


Sin, Cos en Polynomial zijn classes die expressies kunnen bevaten en de waarde die uit de expressie komt bewerkeren met hun eigen functie. Hieronder wat snelle code:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Sin extends AbstractMathExpression
{
        
        public abstract double calculate( double variable ) {
        
                // The check for the 0ne expression should be added in 
                // the add method. So I really don't have to concern me
                // with it now
                //
                Iterator iter = getChilds( );

                double expressionValue = 0;
                        
                while( iter.hasNext ) {
                
                        MathExpression theExpression = (MathExpression) iter.next( );
                        expressionValue = theExpression.calculate( variable );
                }
                
                return expressionValue;
         }
        
}

Het verkrijgen van de waarde van de expressie is gewoon te bereiken door op elk child de methode calculate( ) aan te roepen. Kortom je kan via deze stuctuur expressies makkelijk nesten en wijzigen.

Maar wat is het probleem
Hier komen de problemen. Ik zie enkele punten die me duidelijk moeten zijn voordat ik dit kan implementeren.
• Sommige expressies kunnen maar 1 expressie bevatten ( bijv Sinus ) en andere moeten er meer dan 1 bevatten ( Substract bijvoorbeeld ). Natuurlijk zijn daar trucs voor (bijv optellen + 0, vermenigvuldigen met 1). Zou het duidelijk zijn als ik deze trucs toevoeg of moet ik gewoon een Exception op gaan werpen :?
• Ga ik niet ontzettend in de knoei raken met prioriteiten van de operatoren (plus,min,machtsverheffen)
• Is het instellen van meerdere variabele die nodig zijn (zoals voor een machtsverheffing of een Sommatie reeks ) via de constructor een goed idee ?
• Iemand verder nog opmerkingen over mijn ideeën, mijn ontwerp of anderzijds iets op te merken?

Opbouwende kritiek en opmerkingen vind ik altijd prettig om te horen, ik sta er voor open en barst maar los wat dat betreft. Ook voorbeelden over hoe het mooier kan geschreven in andere talen zijn welkom. Alvast dank voor het lezen van het verhaal en het investeren in van de tijd :)

Verwijderd

Ten eerste: leuk idee!
Zo krijgen we iig het niveau op GoT weer een beetje omhoog..
Leuk met elkaar een beetje discuseren en nieuwe dingen uitwerken :)

Je komt volgens mij nu enorm in de knoop met pioriteiten van je
expressies.
Ik kan me voorstellen dat wanneer dat er vrij veel zijn je toch op
een of andere manier de juiste volgorde eruit moet krijgen.
Misscien is het een idee om geen ArrayList te gebruiken, maar een afgelijde
daarvan die aan de hand van verschillende Operator Objecten zichzelf
indeeld op prioriteit.
Dan krijg je als je de iterator opvraagt van die 'List'altijd je expresies op de
juiste volgorde.

edit: dit is dus geen post van Fewture maar van momania...
zit ff bij Fewture achter z'n pc maar zat dus ff te snurken :Z :)

  • momania
  • Registratie: Mei 2000
  • Laatst online: 23:04

momania

iPhone 30! Bam!

Verwijderd schreef op 04 oktober 2002 @ 23:23:
edit: dit is dus geen post van Fewture maar van momania...
zit ff bij Fewture achter z'n pc maar zat dus ff te snurken :Z :)
Ben weer wakker ;)

Neem je whisky mee, is het te weinig... *zucht*


  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Afbeeldingslocatie: http://members.home.nl/klimstra/expressie.png

Deze code hoort daarbij:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
abstract public class Expression {
    public abstract int evaluate();
}

public class ValueExpression extends Expression {
    private int m_value;
    public ValueExpression(int p_value) {
        m_value = p_value;
    }
    public int evaluate() {
        return m_value;
    }
}

public abstract class BinaryExpression extends Expression {
    private Expression  m_left;
    private Expression  m_right;
    public BinaryExpression(Expression p_left, Expression p_right) {
        m_left = p_left;
        m_right = p_right;
    }
    protected int left() {
        return m_left.evaluate();
    }
    protected int right() {
        return m_right.evaluate();
    }
}

public class MulExpression extends BinaryExpression {
    public MulExpression(Expression p_left, Expression p_right) {
        super(p_left, p_right);
    }
    public int evaluate() {
        return left() * right();
    }
}
// de rest kun je zelf wel bedenken ;)


Dit is een diagram die ik een tijdje geleden heb gemaakt voor een Software-Engineering opdracht. Het is een erg versimpelde versie van de expressie object-structuur van het compilertje waar ik mee bezig ben (zo zijn alle expressies hier van het type int) (overigens is dit ontwerp behoorlijk standaard). Je kunt het uitbreiden met functie-expressies, waar sin, cos, etc weer specialisaties van zijn. Als je wilt kan ik je wel wat voorbeeld code geven.

  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Misschien dat het omzetten van de wiskundige functies (expressie) naar Reverse Polish Notation het gemakkelijker maakt. Dat is de 'normale' gang van zaken voor dit soort problemen. RPN is gemakkelijk uit te rekenen mbv een stack.

1 + 2 -> 1 2 +

De prioriteit van de operatoren moet dus 'opgelost' worden bij het omzetten naar RPN. Dat omzetten is trouwens het post order doorloopen van de expressie boom.

RPN maakt het er alleen niet beter leesbaar op voor de gewone sterveling ;)

We adore chaos because we like to restore order - M.C. Escher


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Na het zien van marcusk's reply denk ik inderdaad dat je nog een stap overgeslagen hebt. Namelijk het klasse diagram maken :) Bijvoorbeeld: Een klasse operator (+-/*), een klasse expressie dat een constante kan zijn (getal) of bestaat uit sub expressies of een operator met een aantal expressies dat die operator nodig heeft. :? hmm...is nog niet helemaal ok, maar iets meer in die richting iig.

We adore chaos because we like to restore order - M.C. Escher


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

Topicstarter
(overleden)
LordLarry schreef op 04 oktober 2002 @ 23:27:
De prioriteit van de operatoren moet dus 'opgelost' worden bij het omzetten naar RPN. Dat omzetten is trouwens het post order doorloopen van de expressie boom.
De boom wordt hier ook in postorder doorlopen, kijk maar mee:

sin geeft zijn childs ( + en cos ) opdracht om te calculaten
+ geeft de opdracht aan + door en aan cos (hmmz die tweede + in mijn boom moet naar rechts, samen met zijn tak)
deze plus begint dan te rekenen met zijn arguementen (zijn childs aan de boom)
De waarde die hier uit komt wordt dan geturned naar plus.

Daarna vraagt de bovenste plus aan cos of hij ook zijn waarde wil berekenen. Dit doet hij met zijn ene argument en returned deze. Als plus deze beide waardes heeft berekent hij zijn deel met de waardes van zijn childs en returned deze naar sin.

Dan gaat sin aan de slag.

Zoals je ziet doorloopt hij dus gewoon alles in postorder ( eerst childs, dan de parent)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Glimi schreef op 04 oktober 2002 @ 23:06:
Aanleiding
Nadat ik enkele hoofdstukken Haskell had doorgebladerd, begon ik me toch af te vragen of dit makkelijke opbouwen van wiskundige expressies niet ongeveer gelijk ingeplementeerd kon worden in Java of any OO taal.
Klinkt zeer interessant en is volgens mij ook een fantastisch topic om leuke discussies op nivo mee te voeren :)
Wat wil ik nu
Ik wil een wiskundige functie kunnen voorstellen zo mooi als dat ook in functionele programmeer talen kan. Hiervoor is misschien wat uitleg nodig

+uitleg code.
Leuk gedaan :) Maar de pure functionele talen die ik ken (clean en haskell) die zijn gebaseerd op de Lambda calculus en daar heb je de volgende structuur.

Term
= Variable
| Function
| Application

Variable
= identifier

Function
= Variable Term

Application
= Term Term

Hierin kan je eigelijk alles wel uitdrukken. Het verschil tussen een operator en een functie kan je meteen korstluiten door in te zien dat een operator niets anders is dan een prefix geschreven functie. In haskell kan je dus bv 10+20 schrijven, maar je kan het ook doen mbv een functie: (+) 10 20. Hierbij moet de + operator tussen haakjes worden gezet zodat je hem als functie kan zien ipv iets.

Daarnaast kan het probleem van functies met meerdere argumenten ook weggehaald worden, door in te zien dat je dus functies kan currien. Ik wil hier nog wel even op ingaan als daar behoefte aan is, maar anders ben ik veel te lang met zo iets vreemds/leuk bezig. En verder als je niet wil currieen dan zou je ook een functie van n argumenten kunnen zien als een functie van 1 argument, maar die een tupel meekrijgt als argument.

Maar voor een niet pure functionele taal zou deze aanpak wel goed toe te passen zijn, en ik heb ook zoiets ontwikkeld voor een expertsysteem voor de RuG.

Ik heb hier zelf ongeveer de volgende structuur voor gebruikt:

interface Expression{Operand calc();}

interface FunctionApplication extends Expression{
...List<Expression> getActualArgs();
...Function getFunction();
}

interface Operand extends Expression{}

interface Variable extends Expression{String getName();}

verder heb je hier nog 100% bewegingsvrijheid in om een implementatie te kiezen :) *houd van interfaces*

Ik ben verder op dit moment vrij veel bezig met functionele talen, de werking van talen en dat soort niet aardse zaken. En als ik nu weer een systeem zou moeten ontwerpen dan zou ik hem totaal anders opzetten (ben op dit moment bezig met een functionle taal gebaseerd op de lambda calculus in Nice).
et verkrijgen van de waarde van de expressie is gewoon te bereiken door op elk
child de methode calculate( ) aan te roepen. Kortom je kan via deze stuctuur
expressies makkelijk nesten en wijzigen.
Het probleem aan deze aanpak is dat je te eager bent om die expressie boom door te rekenen, wat inhoud dat je een ontzettend leuk kenmerk van functionele talen niet kan gebruiken: lazy evaluation. Wat je veel beter kan doen is een 'bakje' terugsturen waaruit alle info zit waarmee het antwoord bepaald kan worden. Zo`n bakje heet een 'thunk'. Vanuit implementatie oogpunt zou je dit ook een expressie kunnen maken, die in zich dus alle info heeft om een waarde te kunnen berkenen, en een veld heeft om de berekende waarde in op te slaan als hij al een keer berekend is, zodat je hem niet nog een keer bepaald.

Maar gewone talen die hanteren wel meer jouw mechanisme (zo heb ik hem zelf ook toegepast). Alhoewel je die toegevoegde functionaliteit aan deze reeks ook perfect had kunnen toevoegen met visitors ;) (je krijgt dan alleen wel een vet grote visitor :+ )
• Sommige expressies kunnen maar 1 expressie bevatten ( bijv Sinus ) en andere
moeten er meer dan 1 bevatten ( Substract bijvoorbeeld ). Natuurlijk zijn daar
trucs voor (bijv optellen + 0, vermenigvuldigen met 1). Zou het duidelijk zijn
als ik deze trucs toevoeg of moet ik gewoon een Exception op gaan werpen :?
Als je het op de 'niet functionele' manier wilt doen, maar op de jouwe, dan zou ik in 1e instantie gaan voor alleen functies (en zo heb je dus meteen operatoren) die n formele parameters hebben en een return type. Je hebt dan meteen een FunctionSignature. En verder heb je nog een FunctionApplication, waarmee je dus de functie voorziet van invoer (mbv actuele parameters). Daarnaast zou ik ook me niet al te druk gaan maken over hoeveel parameters dat dan nou zijn, en alleen als snelheid en geheugenverbruik een belangrijke factor gaan worden, dan zou ik pas het generieke ontwerp wat specifieker maken.
• Ga ik niet ontzettend in de knoei raken met prioriteiten van de operatoren
(plus,min,machtsverheffen)
Aan de hand van de prioriteiten wordt de juiste expressie boom opgebouwd. Dus die expressie boom die is dus al correct. En het correct opbouwen van die expressieboom die kan perfect plaats vinden in de parser.
• Is het instellen van meerdere variabele die nodig zijn (zoals voor een
machtsverheffing of een Sommatie reeks ) via de constructor een goed idee ?
ik neem dus aan dat je dit bedoelt met een somatie reeks:
1+1+1+1
Ik zou zo weinig mogelijk niets toevoegende structuren toevoegen en zou het als volgt oplossen:
1+(1+(1+1)))
Deze moet je me nog maar even goed uitleggen ;)
Iemand verder nog opmerkingen over mijn ideeën, mijn ontwerp of anderzijds
iets op te merken?
Ik ben blij dat je zoveel interesse toont voor deze materie, en ik ben ook blij dat je het GoF boek aan het doorploegen bent en helemaal blij dat je deze dingen met elkaar combineerd.

*made with emacs ;)

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Trouwens, had mbravenboer laatst niet iets vergelijkbaars gemaakt ? (ik kan het topic zo snel niet vinden)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
LordLarry schreef op 04 oktober 2002 @ 23:27:
Misschien dat het omzetten van de wiskundige functies (expressie) naar Reverse Polish Notation het gemakkelijker maakt. Dat is de 'normale' gang van zaken voor dit soort problemen. RPN is gemakkelijk uit te rekenen mbv een stack.

1 + 2 -> 1 2 +

De prioriteit van de operatoren moet dus 'opgelost' worden bij het omzetten naar RPN. Dat omzetten is trouwens het post order doorloopen van de expressie boom.

RPN maakt het er alleen niet beter leesbaar op voor de gewone sterveling ;)
Heeft wel wat weg van Forth. ( Bijna Chinees als je een redelijk progje hebt :) )

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:40
offtopic:
Is ie daar weer met z'n wiskunde?
;) :+

https://fgheysels.github.io/


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

farlane schreef op 05 oktober 2002 @ 01:17:
Heeft wel wat weg van Forth. ( Bijna Chinees als je een redelijk progje hebt :) )
Is gewoon een postfix geschreven expressie :)

  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

Reverse Polish Notation is wel het handigst om een expressie te evalueren. Een functie omzetten naar deze notatie is niet echt lastig, er is een redelijk simpel algoritme voor (ben er laatst zelf mee bezig geweest om een simpele expressie evaluator te maken, kan je het algoritme wel uitleggen als het nodig is).

Als je eenmaal de expressie in RPN hebt kun je deze evalueren door een stack te gebruiken. Als je een variabele of getal (x, 7) tegenkomt, push je em bovenop de stack, bij een operator (+, *, ^2) voer je deze operatie uit op de getallen bovenaan de stack (deze haal je er ook meteen af), en zet je het resultaat weer bovenop de stack. Nu denk ik dat je deze methode ook wel kunt gebruiken om een tree te maken die zich houdt aan alle prioriteiten. Voorbeeltje:

Jouw functie:
code:
1
sin( x^2 + 7x + cos( 30x ) )

De RPN notatie van deze functie:
code:
1
x  ^2  7  x  *  +  30  x  * cos  +  sin


Zo zou de RPN geevalueerd worden:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(stack geeft de huidige staat van de stack aan,
 de top zit rechts en elk element zit tussen [haken])
stack: 
> push x
stack: [x]
> voer ^2 uit (pop a, push a^2)
stack: [x^2]
> push 7
stack: [x^2] [7]
> push x
stack: [x^2] [7] [x]
> voer * uit (pop a, pop b, push b*a)
stack: [x^2] [7*x]
> voer + uit (pop a, pop b, push b+a)
stack: [x^2+7*x] 
> push 30
stack: [x^2+7*x] [30]
> push x
stack: [x^2+7*x] [30] [x]
> voer * uit (pop a, pop b, push b*a)
stack: [x^2+7*x] [30*x]
> voer cos uit (pop a, push cos(a))
stack: [x^2+7*x] [cos(30*x)]
> voer + uit (pop a, pop b, push b+a)
stack: [x^2+7*x+cos(30*x)]
> voer sin uit (pop a, push sin(a))
stack: [sin(x^2+7*x+cos(30*x))]

Zoals je ziet is aan het eind de oorspronkelijke functie weer ontstaan.
(heb trouwens ^2 als 'kwadraat operatie' gezien, eigenlijk zou je de operator ^ los moeten zien en 2 als een operand)

Als je nu bij elke waarde (var, getal) en operatie een nieuwe treenode maakt, en bij de operatie-nodes verbinding maakt met de nodes die de waarden voor de operatie bevatten, krijg je zoiets:
Afbeeldingslocatie: http://www.theforumisdown.com/uploadfiles/0802/rpn.gif
Deze tree klopt volgens mij gegarandeerd wat betreft de prioriteiten en is vanuit de RPN notatie gezien vrij simpel te maken.

www.madwizard.org


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

*kick* :)

  • Knutselsmurf
  • Registratie: December 2000
  • Laatst online: 18:23

Knutselsmurf

LED's make things better

Toevallig zit ik ook met een zelfde idee te worstelen en in mijn opzet heb ik 1 grote aanvulling hierop. Alle variabelen (x in het voorbeeld) krijgen bij mij een aparte plaats in een lijst. Het enige dat het object doet, die geacht wordt de waarde van x terug te geven is de waarde retourneren waarnaar wordt verwezen in de lijst. Op deze wijze kun je, door 1 waarde in deze lijst te wijzigen, de functie snel opnieuw evalueren voor nieuwe waarden van x.

Verder heb ik een basisklasse, die niet veel meer bevat dan een evaluate-functie. In de hiervan afgeleide classes wordt hieraan zinnige code toegevoegd, afhankelijk van de functie.

Bijvoorbeeld de evaluate-functie van een sin-object zal als volgt zijn:

result:=sin(argument.evaluate);

en van een optelling:

result:=argument1.evaluate+argument2.evaluate;


* Knutselsmurf gooit dit topic in z'n bookmarks....

- This line is intentionally left blank -


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

Topicstarter
(overleden)
Verwijderd schreef op 04 oktober 2002 @ 23:23:
Je komt volgens mij nu enorm in de knoop met pioriteiten van je
expressies.
Ik kan me voorstellen dat wanneer dat er vrij veel zijn je toch op
een of andere manier de juiste volgorde eruit moet krijgen.
Misscien is het een idee om geen ArrayList te gebruiken, maar een afgelijde
daarvan die aan de hand van verschillende Operator Objecten zichzelf
indeeld op prioriteit.
Dan krijg je als je de iterator opvraagt van die 'List'altijd je expresies op de
juiste volgorde.
Zoals ik hierboven al uitlegde, zijn er geen problemen met prioriteiten omdat je een boom opbouwt en argumenten mee geeft aan functies. Je ziet operatoren dan ook niet als operatoren maar als functies (zoals Alarmnummer ook al zei, net als in Haskell kan)
Verder lijken prioriteiten me geen verantwoordelijkheid van deze structuur maar eerder van de parser. Als de boom eenmaal in elkaar zit, dan is er geen probleem meer.
marcusk schreef op 04 oktober 2002 @ 23:26:
afbeelding
Dit is een diagram die ik een tijdje geleden heb gemaakt voor een Software-Engineering opdracht. Het is een erg versimpelde versie van de expressie object-structuur van het compilertje waar ik mee bezig ben (zo zijn alle expressies hier van het type int) (overigens is dit ontwerp behoorlijk standaard). Je kunt het uitbreiden met functie-expressies, waar sin, cos, etc weer specialisaties van zijn. Als je wilt kan ik je wel wat voorbeeld code geven.
Ik begrijp duidelijk waar je naar toe wilt, maar het staat me gewoon niet aan om voor elke combinatie ( functie met 2 argumenten, functie met 3 argumenten, functie met slechts 1 argument, functie met max x argumenten ed. ed. ) aparte subclasses aan te maken. Zo krijg je imho een hele sloot aan subclasses terwijl het ook generieker moet kunnen. Ik denk er bijvoorbeeld ook aan om alles te implementeren met 1 argument, welke dan weer een nieuwe functie oplevert. Dit principe heet currying naar Haskell Curry. Zie voor meer info hierover het 'haskell tutorial' topic wat hier pas langs kwam
whoami schreef op 05 oktober 2002 @ 10:35:
offtopic:
Is ie daar weer met z'n wiskunde?
;) :+
Pfff, ik maak tenminste dingen waar _niemand_ wat aan heeft!
madwizard schreef een verhaal over RPN
Feitelijk evalueert mijn methode ook naar een RPN notatie 8)
Vanavond komt mijn reply op uw stuk. Als ik die URL ff goed doorgelezen heb iig :)

  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 16-05 13:05
Hier is een design pattern op toe te passen. En wel het Composite pattern. MarcusK's plaatje is er een voorbeeldje van. Het Composite pattern kun je goed uitbreiden met het Decorator pattern. Een decorator stelt je in staat om dynamisch eigenschappen toe te voegen aan je composite structuur. Exacte nut van een Decorator in dit geval kan ik zo even neit bedenken.

In ieder geval, ik adviseer je om is te kijken naar wat info over het composite pattern. Wat je daarmee doet is een standaard object te definiëren. Dit standaard object bevat alle methoden die nodig zijn om in een tree te kunnen passen. Dit standaard object is abstract. Vervolgens erf je van dit geval diverse composites en leafs over. Een container is een node in je tree die 1,2,3 of meer componenten kan bevatten, net wat jij wil. De leafs zijn eind node's.

In jou geval lijkt het mij het beste om als leafs daadwerkelijke waarden te gebruiken. Dus een constante of een variabele (1, 2, PI, x,y, etc.) Vervolgens maak je voor alle overigen een composite. Bijvoorbeeld een sinus composite. Deze moet een afgeleide van het standaard object bevatten. Dit is dus weer een composite of een leaf.

Ik neem aan dat je de rest nu verder zelf kan verzinnen.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

The - DDD schreef op 06 oktober 2002 @ 20:13:
Hier is een design pattern op toe te passen. En wel het Composite pattern. MarcusK's plaatje is er een voorbeeldje van. Het Composite pattern kun je goed uitbreiden met het Decorator pattern. Een decorator stelt je in staat om dynamisch eigenschappen toe te voegen aan je composite structuur. Exacte nut van een Decorator in dit geval kan ik zo even neit bedenken.
Ik ook niet :)
In ieder geval, ik adviseer je om is te kijken naar wat info over het composite pattern. Wat je daarmee doet is een standaard object te definiëren. Dit standaard object bevat alle methoden die nodig zijn om in een tree te kunnen passen. Dit standaard object is abstract.
iekss... abstracte objecten boven aan een type hierarchie ;) Je kunt veel beter een interface gebruiken zodat je alle vrijheid behoud om zelf een implementatie te kiezen :) En verder vind ik het compositie design pattern eigelijk helemaal niet zo geweldig. Je krijgt namelijk een spreiding van functionaliteit over een groot aantal classes heen wat de inzicht op die functionaliteit niet ten goeie komt en je krijgt er ook van die grote rot classes door. Veel 'composite' functies kan je beter vervangen door een visitor structuur, zodat je wel een bundeling van functionaliteit krijgt :)
In jou geval lijkt het mij het beste om als leafs daadwerkelijke waarden te gebruiken. Dus een constante of een variabele (1, 2, PI, x,y, etc.) Vervolgens maak je voor alle overigen een composite. Bijvoorbeeld een sinus composite. Deze moet een afgeleide van het standaard object bevatten. Dit is dus weer een composite of een leaf.

Ik neem aan dat je de rest nu verder zelf kan verzinnen.
Anders moet je mijn reply nog even doorkijken :) Ik heb al een voorbeeldje gegeven van een mogelijke aanpak die hier redelijk mee overeenkomt En ik zou dus echt visitors gebruiken (of een echte taal zoals Nice) om die functioniteit toe te gaan voegen. Naarmate het systeem verder gaat evolueren begin je echt te verzuipen in de methodes en je krijgt een enorme spreiding van functioniteit.

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

Topicstarter
(overleden)
Alarmnummer schreef op 05 oktober 2002 @ 00:22:
Klinkt zeer interessant en is volgens mij ook een fantastisch topic om leuke discussies op nivo mee te voeren :)
Nivo m'n aarsch! Ik wil een oplossing en snel verdomme >:) :+
Leuk gedaan :) Maar de pure functionele talen die ik ken (clean en haskell) die zijn gebaseerd op de Lambda calculus en daar heb je de volgende structuur.

Term
= Variable
| Function
| Application

Variable
= identifier

Function
= Variable Term

Application
= Term Term
Volledigheid: Term is dus bijv x, Function is een functie van x en een application is dan f( 3 ) bijv. Dit scheid ik inderdaad nog niet, en gooi het op een grote hoop. Ik wou dat nog wel gaan doen, maar dan zal ik eerst lambda c. gaan bestuderen om een beter beeld te krijgen. Ik merk nu namelijk dat ik dit zit te doen zonder een echte diepgaande kennis van de materie, het is dus een beetje verkennen. Ik houdt het ff in mijn achterhoofd.
Hierin kan je eigelijk alles wel uitdrukken. Het verschil tussen een operator en een functie kan je meteen korstluiten door in te zien dat een operator niets anders is dan een prefix geschreven functie. In haskell kan je dus bv 10+20 schrijven, maar je kan het ook doen mbv een functie: (+) 10 20. Hierbij moet de + operator tussen haakjes worden gezet zodat je hem als functie kan zien ipv iets.
Dit had ik al wel door en zit ook geïmplementeerd in het huidige ontwerp.
Daarnaast kan het probleem van functies met meerdere argumenten ook weggehaald worden, door in te zien dat je dus functies kan currien. Ik wil hier nog wel even op ingaan als daar behoefte aan is, maar anders ben ik veel te lang met zo iets vreemds/leuk bezig. En verder als je niet wil currieen dan zou je ook een functie van n argumenten kunnen zien als een functie van 1 argument, maar die een tupel meekrijgt als argument.
Ik zie eigenlijk wel een manier om curring te introduceren binnen dit ontwerp toe te passen. Neem bijvoorbeeld de functie 'Power'. Die heeft als argument nodig tot welke macht hij gaat verheffen. Dit lijkt me eigenlijk erg simpel binnen een constructie van een objectfunctie toe te passen door het gewoon inde constructor te hangen.
Maar voor een niet pure functionele taal zou deze aanpak wel goed toe te passen zijn, en ik heb ook zoiets ontwikkeld voor een expertsysteem voor de RuG.

Ik heb hier zelf ongeveer de volgende structuur voor gebruikt:

interface Expression{Operand calc();}

interface FunctionApplication extends Expression{
...List<Expression> getActualArgs();
...Function getFunction();
}

interface Operand extends Expression{}

interface Variable extends Expression{String getName();}

verder heb je hier nog 100% bewegingsvrijheid in om een implementatie te kiezen :) *houd van interfaces*
Niet helemaal duidelijk, misschien is dit nog te verduidelijken met een klein class diagramm, maar opzich snap ik hem wel redelijk. Echter wat is uw verschil tussen een Variable en een operand.
Ik ben verder op dit moment vrij veel bezig met functionele talen, de werking van talen en dat soort niet aardse zaken. En als ik nu weer een systeem zou moeten ontwerpen dan zou ik hem totaal anders opzetten (ben op dit moment bezig met een functionle taal gebaseerd op de lambda calculus in Nice).
Keep us informed zou ik zeggen :D
Het probleem aan deze aanpak is dat je te eager bent om die expressie boom door te rekenen, wat inhoud dat je een ontzettend leuk kenmerk van functionele talen niet kan gebruiken: lazy evaluation. Wat je veel beter kan doen is een 'bakje' terugsturen waaruit alle info zit waarmee het antwoord bepaald kan worden. Zo`n bakje heet een 'thunk'. Vanuit implementatie oogpunt zou je dit ook een expressie kunnen maken, die in zich dus alle info heeft om een waarde te kunnen berkenen, en een veld heeft om de berekende waarde in op te slaan als hij al een keer berekend is, zodat je hem niet nog een keer bepaald.

Maar gewone talen die hanteren wel meer jouw mechanisme (zo heb ik hem zelf ook toegepast). Alhoewel je die toegevoegde functionaliteit aan deze reeks ook perfect had kunnen toevoegen met visitors ;) (je krijgt dan alleen wel een vet grote visitor :+ )
Lazy evaluation, idd dat zou de boel een stuk l33ter maken dan de huidige math functies van java 8) Echter zoals ik hierboven al stel, bevat ik de theorie niet geneog om dat op het moment te kunnen implementeren. Echter ik zet hem op de lijst voor versie 1.1 :D Samen met differentiatie van een functie
Als je het op de 'niet functionele' manier wilt doen, maar op de jouwe, dan zou ik in 1e instantie gaan voor alleen functies (en zo heb je dus meteen operatoren) die n formele parameters hebben en een return type. Je hebt dan meteen een FunctionSignature. En verder heb je nog een FunctionApplication, waarmee je dus de functie voorziet van invoer (mbv actuele parameters). Daarnaast zou ik ook me niet al te druk gaan maken over hoeveel parameters dat dan nou zijn, en alleen als snelheid en geheugenverbruik een belangrijke factor gaan worden, dan zou ik pas het generieke ontwerp wat specifieker maken.
Je bedoelt hier gewoon functies maken die een 0...n aantal expressies accepteren en dan gewoon uitzoeken wat ze er mee moeten. Later kunnen die altijd nog naar 1...n expressies of 1 ed. Lijkt me een goed plan :)
ik neem dus aan dat je dit bedoelt met een somatie reeks:
1+1+1+1
Ik zou zo weinig mogelijk niets toevoegende structuren toevoegen en zou het als volgt oplossen:
1+(1+(1+1)))
Deze moet je me nog maar even goed uitleggen ;)
Hier is die dan: sommatiereeksen, voor de syntax moet je even http://oege.ie.hva.nl/~schie14/k.htm zien. Wat je met deze notatie doet is een functie (in dit geval nx^2) voor elke n in [n...k] de functie uitvoeren en bij de vorige uitvoeren optellen. n begint hier op n en stel k is 4 dan is de uitvoer van deze functie met x = 2: 0 + 4 + 8 + 12 + 16 = 40
Ik ben blij dat je zoveel interesse toont voor deze materie, en ik ben ook blij dat je het GoF boek aan het doorploegen bent en helemaal blij dat je deze dingen met elkaar combineerd.

*made with emacs ;)
Je hebt me besmet :+ En emacs *bibber*

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 23:53

Reptile209

- gers -

Glimi schreef op 06 oktober 2002 @ 22:58:
[...]
Hier is die dan: sommatiereeksen, voor de syntax moet je even http://oege.ie.hva.nl/~schie14/k.html zien.
Linkje is dood: HTTP 404 - File not found :'(

Zo scherp als een voetbal!


  • thomaske
  • Registratie: Juni 2000
  • Laatst online: 19-05 09:52

thomaske

» » » » » »

Brusselmans: "Continuïteit bestaat niet, tenzij in zinloze vorm. Iets wat continu is, is obsessief, dus ziekelijk, dus oninteressant, dus zinloos."


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

Topicstarter
(overleden)
Even de URL bijgewerkt (en we wachten op Alarmnummer :+ )
*kick*

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Ik heb eerlijk gezegd niet meer zoveel toe te voegen :)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Oja.. ik heb nog een reden waarom het composite design pattern niet zo geweldig is en dat is dat over het algemeen de traversal over een object niet wordt hergebruikt. In iedere methode zal die traversal geplaatst worden en het is veel handiger om die te gaan hergebruiken zoals je dat ook kan doen met een fold functie uit haskell of de visitor guide uit oo.

Kom ik meteen iets op iets waar ik al een tijdje mee in gedachten loop te spelen: zichzelf beschrijvende datastructuren.
Pagina: 1