Java operator precedence

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • uppie8374
  • Registratie: Maart 2002
  • Laatst online: 16-09 13:22
Bij een oefenvraag kreeg ik het volgende:

boolean x = ((5<7) || (++count < 10)); waarbij count start met de waarde 0.

Het resultaat was x is true en count blijft 0.

Dit snap ik niet aangezien bij de operator precedence als eerste in de binnenste haakjes wordt gewerkt, en bovendien de increment operators op de eerste plek staan wat betreft verwerking. In mijn ogen hoort het dus dit te doen:

code:
1
2
3
4
((5<7) || (++count < 10))
(true || (++count < 10))
(true || (1 < 10))           -- count wordt hier 1
(true || true)               -- pas hier zijn alle binnenste haakjes weggewerkt en wordt enkel de linker operand bekeken.


Waar gaat mijn logica de mist in?

Rebuilding knowledge database

Alle reacties


Acties:
  • +2 Henk 'm!

  • decipherer
  • Registratie: Februari 2002
  • Laatst online: 00:36
Op regel 2 kan al bepaald worden dat het statement true oplevert en zal niet verder geëvalueerd worden. Zo werkt het in ieder geval in C#, verwacht dat het in Java dan ook zo werkt.

Het fenomeen wordt short-circuit(ing) genoemd

[ Voor 38% gewijzigd door decipherer op 11-12-2018 21:26 ]

De beste ideeën komen als je bezig bent.


Acties:
  • 0 Henk 'm!

  • uppie8374
  • Registratie: Maart 2002
  • Laatst online: 16-09 13:22
Dat betekent dus dat, ondanks dat de precedence van || pas een van de laatste is, het rechterdeel niet meer wordt bekeken zodra er links al een true staat?

Ik verwachte dat alle operatoren netjes op hun 'beurt' wachten.

Rebuilding knowledge database


Acties:
  • +1 Henk 'm!

  • decipherer
  • Registratie: Februari 2002
  • Laatst online: 00:36
Java zal het tweede operand niet evalueren tenzij het absoluut noodzakelijk is:
Short circuiting. When using the conditional and and or operators (&& and ||), Java does not evaluate the second operand unless it is necessary to resolve the result. This allows statements like if (s != null && s.length() < 10) to work reliably. Programmers rarely use the non short-circuiting versions (& and |) with boolean expressions.
Zie ook: https://introcs.cs.princeton.edu/java/11precedence/

De beste ideeën komen als je bezig bent.


Acties:
  • +1 Henk 'm!

  • Taghorn
  • Registratie: Augustus 2011
  • Laatst online: 06-10 08:53
Wat decipherer zegt klopt.
De operators wachten netjes op hun beurt. De computer ziet een || of een && en gaat dan eerst de linker tak uitrekenen. Want dat moet voordat je een || kunt evalueren (je moet ook rechts uitrekenen, maar je kunt maar 1 ding tegelijk, dus eerst links)
Zodra er 'true || (whatever)' staat weet de computer dat het geheel true word en stopt. Waarom rekenen als het niet nodig is?

Leuk geintje hiermee is: boolean x = (a <> 0 && (b / a) > 5 )
Dit gaat ook goed als a == 0, want het rechterdeel word nooit uitgerekend

(edit: || gebruikt in voorbeeld ipv &&)

[ Voor 4% gewijzigd door Taghorn op 12-12-2018 13:16 . Reden: (edit: || gebruikt in voorbeeld ipv &&) ]


Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Echter, dan moet je niet or gebruiken maar and

Acties:
  • 0 Henk 'm!

  • uppie8374
  • Registratie: Maart 2002
  • Laatst online: 16-09 13:22
Zo ook de volgende expressie:

(2 > 3) && (5 < 2) | true

Hier schijnt niets achter && te worden geevalueerd omdat 2 > 3 al false is, ondanks dat | een hogere precedence heeft.

Ok bovenstaande snap ik wanneer de regel is dat alles na && niet meer geevalueerd wordt zodra de 2 > 3 false blijkt te zijn. Maar nu teste ik nog even verder met de volgende expressie:

(3 > 9) | (4 > 4) && (1 == 1) || (2 == 2);

En hier kwam gewoon true uit, terwijl alles voor de && toch duidelijk false is.

[ Voor 46% gewijzigd door uppie8374 op 11-12-2018 22:01 ]

Rebuilding knowledge database


Acties:
  • +1 Henk 'm!

  • Merethil
  • Registratie: December 2008
  • Laatst online: 21:45
uppie8374 schreef op dinsdag 11 december 2018 @ 21:39:
Zo ook de volgende expressie:

(2 > 3) && (5 < 2) | true

Hier schijnt niets achter && te worden geevalueerd omdat 2 > 3 al false is, ondanks dat | een hogere precedence heeft.

Ok bovenstaande snap ik wanneer de regel is dat alles na && niet meer geevalueerd wordt zodra de 2 > 3 false blijkt te zijn. Maar nu teste ik nog even verder met de volgende expressie:

(3 > 9) | (4 > 4) && (1 == 1) || (2 == 2);

En hier kwam gewoon true uit, terwijl alles voor de && toch duidelijk false is.
Hier loop je tegen een gebrek aan haakjes aan, hij doet dit volgens operator precedence:

code:
1
(((3>9) | (4>4)) && 1==1) || (2==2)


Die laatste na de logical OR zorgt voor true.

Acties:
  • 0 Henk 'm!

  • uppie8374
  • Registratie: Maart 2002
  • Laatst online: 16-09 13:22
Het klopt dat er geen haakjes in staan... ik ben aan het oefenen voor mijn OCA, waar ze proberen het zo onduidelijk mogelijk te maken.

Rebuilding knowledge database


Acties:
  • 0 Henk 'm!

  • Aetos
  • Registratie: November 2001
  • Laatst online: 22:19
Ze proberen je te laten merken dat indien je de definitie documenten goed kent je zonder haakjes ook precies kan weten wat er zal gebeuren.

Acties:
  • 0 Henk 'm!

  • Merethil
  • Registratie: December 2008
  • Laatst online: 21:45
uppie8374 schreef op dinsdag 11 december 2018 @ 22:19:
Het klopt dat er geen haakjes in staan... ik ben aan het oefenen voor mijn OCA, waar ze proberen het zo onduidelijk mogelijk te maken.
Dat is ook prima, ik voegde de haakjes toe om te verduidelijken hoe de compiler jouw code opvat. De precedence zorgt dus voor de true die je kreeg, ook al gokte je dat je die niet zou krijgen.

Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

uppie8374 schreef op dinsdag 11 december 2018 @ 22:19:
Het klopt dat er geen haakjes in staan... ik ben aan het oefenen voor mijn OCA, waar ze proberen het zo onduidelijk mogelijk te maken.
Bedenk vooral dat short-circuiting werkt op 1 operator tegelijk. Dus ondanks dat er short circuiting van toepassing kan zijn bij een operator, betekent dat nog niet dat de rest van de evaluatie niet doorgaat, afhankelijk van de operator precedence.

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Of je kijkt gewoon even hier ;)

Verder, als je aan 't testen bent zijn variabelen als a, b en c niet heel handig; je maakt beter een method test("a", true) die het eerste argument print en het tweede argument returned. Dan kun je daarna dingen doen als test("a", true) || test("b", false) en zul je alleen a zien verschijnen in je output en dus weet je dat het tweede deel nooit geëvalueerd wordt.

code: pseudo
1
2
3
4
func Test(string value, bool retvalue) {
  print value;
  return retvalue;
}


De rule-of-thumb is gewoon: zodra met 100% zekerheid vaststaat wat de uitkomst van de expressie wordt wordt er niet meer verder gekeken. Simple as that.

[ Voor 20% gewijzigd door RobIII op 11-12-2018 22:56 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • +1 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 06-10 13:59
Dit is hoe het in de meeste talen werkt en dat heeft een goeie reden:
code:
1
2
3
if(someValue == null || someValue.isEmpty()) {
    //Do something
}

Als bovenstaande niet zou short-circuiten zou je op 't 2e deel een null pointer exception krijgen. Dit is by design.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • uppie8374
  • Registratie: Maart 2002
  • Laatst online: 16-09 13:22
Ik ben bekend met het gegeven dat een short-circuit operator de tweede operand pas evalueert nadat het resultaat van de eerste bekend is. Maar het lastige is wanneer je daarbij ook nog rekening moet houden met de standaard precedence volgorde.

Wanneer je kijkt naar de precedence dan zouden bij de volgende expressie beide operatoren aan de rechterkant van de && voorrang krijgen boven de operator aan de linkerkant. Toch wordt dus als ik het goed begrijp de linkerkant eerst geëvalueerd, aangezien het om een short-circuit gaat. En dat terwijl de && operator in deze vergelijking de laagste precedence uit de hele vergelijking heeft.

(5 == 6) && (++x > 1) , waarbij bv x = 3;

Rebuilding knowledge database


Acties:
  • 0 Henk 'm!

  • ThomasG
  • Registratie: Juni 2006
  • Laatst online: 23-09 14:00
Zowel || als && operatoren (kunnen) short-circuiten, alleen dan omgekeerd. Een OR-expressie short-circuit bij een true en een AND-expressie short-circuit bij een false. Aangezien in jouw voorbeeld 5 == 6 onwaar is, zal de rest dus niet worden uitgevoerd.

Een vuistregel is dat de logical operators short-circuiten, en de boolean logical operators niet; zij voeren altijd alle expressies onafhankelijk van elkaar uit, en vergelijken dan pas het resultaat.

Het volgende voorbeeld is logical OR, voert het in "serie" uit, en en zal dus short-circuiten.
Java:
1
boolean x = ((5<7) || (++count < 10));


Wanneer je boolean logical OR gebruikt, voert het echter "parallel" uit, en zal dus niet (kunnen) short-circuiten.
Java:
1
boolean x = ((5<7) | (++count < 10));


En zo verwarrend is het niet qua presidence. Je hebt denk ik een verkeerd beeld van de precedence. Laten we het voorbeeld: (5 == 6) && (++x > 1) nemen. Waarschijnlijk denk jij, omdat de unary pre-increment hoger in de precedence tabel staat, dat het in dit geval als eerste uitgevoerd zal worden. Dat is niet het geval, omdat het tot een andere expressie behoord. De eerste expressie die zal worden uitgevoerd is: expression && expression. ++x > 1 is in dit geval namelijk een sub-expressie van de logical and operator, omdat het het linker deel van de expressie is zal het enkel worden uitgevoerd als de linker expressie true geeft.

[ Voor 3% gewijzigd door ThomasG op 12-12-2018 11:10 ]


Acties:
  • 0 Henk 'm!

  • uppie8374
  • Registratie: Maart 2002
  • Laatst online: 16-09 13:22
ThomasG schreef op woensdag 12 december 2018 @ 11:09:
Waarschijnlijk denk jij, omdat de unary pre-increment hoger in de precedence tabel staat, dat het in dit geval als eerste uitgevoerd zal worden.
Dat is inderdaad precies wat mijn gedachte was. Met als volgorde:
1. Unary (de ++)
2. Multiplication, Division, Modulus
3. Addition, Subtraction
4. Relational (de >)
5. Equality (de ==)
6. Logical in de volgorde &, ^, |
7. Short-Circuit in de volgorde &&, || (de &&).
8. Assignment

Komt de && pas als laatste aan de beurt.

Rebuilding knowledge database


Acties:
  • +1 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 16:38

.oisyn

Moderator Devschuur®

Demotivational Speaker

uppie8374 schreef op woensdag 12 december 2018 @ 10:40:
Ik ben bekend met het gegeven dat een short-circuit operator de tweede operand pas evalueert nadat het resultaat van de eerste bekend is. Maar het lastige is wanneer je daarbij ook nog rekening moet houden met de standaard precedence volgorde.

Wanneer je kijkt naar de precedence dan zouden bij de volgende expressie beide operatoren aan de rechterkant van de && voorrang krijgen boven de operator aan de linkerkant. Toch wordt dus als ik het goed begrijp de linkerkant eerst geëvalueerd, aangezien het om een short-circuit gaat. En dat terwijl de && operator in deze vergelijking de laagste precedence uit de hele vergelijking heeft.
Precedence heeft niets te maken met evaluatie-volgorde. Het bepaalt puur waar de virtuele haakjes komen te staan.
Java:
1
int i = foo() + bar() * baz();

Hier worden foo, bar en baz gewoon aangeroepen in die volgorde. De resultaten van bar en baz worden wel eerst vermenigvuldigd voordat het bij het resultaat van foo wordt opgeteld, omdat de * een hogere precedence heeft dan de +, maar dat doet niets af aan het feit dat de evaluatievolgorde van de afzonderlijke termen nog steeds van links naar rechts is.

[ Voor 19% gewijzigd door .oisyn op 12-12-2018 11:52 ]

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.


Acties:
  • 0 Henk 'm!

  • uppie8374
  • Registratie: Maart 2002
  • Laatst online: 16-09 13:22
Dus het beste om te doen is de haakjes neerzetten volgens de precedence, en dan eerst de linkerkant uitrekenen van && of || ?

Rebuilding knowledge database


Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Als je de haakjes hebt gezet gewoon van links naar rechts. En als een statement zonder haakjes verwarrend is, gewoon haakjes zetten.

Acties:
  • 0 Henk 'm!

  • dcm360
  • Registratie: December 2006
  • Niet online

dcm360

Moderator Discord

HD7767 powered

.oisyn schreef op woensdag 12 december 2018 @ 11:45:
[...]

Precedence heeft niets te maken met evaluatie-volgorde. Het bepaalt puur waar de virtuele haakjes komen te staan.
Java:
1
int i = foo() + bar() * baz();

Hier worden foo, bar en baz gewoon aangeroepen in die volgorde. De resultaten van bar en baz worden wel eerst vermenigvuldigd voordat het bij het resultaat van foo wordt opgeteld, omdat de * een hogere precedence heeft dan de +, maar dat doet niets af aan het feit dat de evaluatievolgorde van de afzonderlijke termen nog steeds van links naar rechts is.
De evaluatievolgorde van expressies is niet gedefinieerd zolang de precedence maar gevolgd wordt. De uitzondering hierop zijn function calls, die moeten altijd van links naar rechts. In pseudo-assembly wordt jouw code als volgt:
code:
1
2
3
4
5
6
call foo;
call bar;
call baz;
mul;
add;
store i;

Als ik het echter omschrijf zonder de function calls, krijg ik twee mogelijkheden voor de (wederom pseudo)-assembly:
Java:
1
int i = a + b * c;

code:
1
2
3
4
5
6
load a;
load b;
load c;
mul;
add;
store i;
en
code:
1
2
3
4
5
6
load b;
load c;
mul;
load a;
add;
store i;

In mijn voorbeeld verandert de betekenis van de code niet door de volgorde te veranderen, maar als je de tweede variant doet met function calls is dat niet te garanderen (want een function call kan side effects hebben).

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 16:38

.oisyn

Moderator Devschuur®

Demotivational Speaker

dcm360 schreef op woensdag 12 december 2018 @ 23:37:
[...]

De evaluatievolgorde van expressies is niet gedefinieerd
Dat is simpelweg onjuist.
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.

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.


Acties:
  • +1 Henk 'm!

  • ThomasG
  • Registratie: Juni 2006
  • Laatst online: 23-09 14:00
uppie8374 schreef op woensdag 12 december 2018 @ 19:36:
Dus het beste om te doen is de haakjes neerzetten volgens de precedence, en dan eerst de linkerkant uitrekenen van && of || ?
In principe zijn de logical and- en logical-or operators left-to-right, en zijn gedefineerd als expression && expression en expression || expression.

Als je een expressie tegen komt als: a && b, dan weet je dat a als eerste wordt geevalueerd. Als je weet dat a altijd false is, hoef je dus in principe niet naar b te kijken.

Als je twee logical and-operators gaan combineren, bijvoorbeeld: a && b && c dan wordt het al iets moeilijker. In de precedence tabel staat ook de associativity (ofwel, als er meerdere operators zijn met dezelfde precedence geeft het aan welke richting je op leest). Bij de logical and-operator is dat van links naar rechts. Eigenlijk staat er dus: a && (b && c).

Stel, nu gaan we het combineren met een logical or-operator. a && b || c && d. Wij weten dat de logical or-operator een lagere precedence heeft dan de logical and-operator. Het eerste wat we tegen komen is een logical and-operator. De linker kant is eenvoudig, dat is a. De rechterkant is iets moeilijker. We weten dat de b erbij hoort, maar hoort de logical or-operator er ook bij? Nee, want de logical or-operator heeft een lagere precedence; daardoor hebben we in feiten te maken met een sub-expressie. We kunnen nu dus haakjes om a && b zetten, en weten dus het linker deel van de logical or-operator. Datzelfde doen we voor het rechterdeel, en komen dus tot: (a && b) || (c && d).

Ditzelfde kun je doen met expressies als: a < b && c. We weten dat de "kleiner dan"-operator een hogere precedence heeft dan de logical and-operator. Als we er haakjes omheen zetten komen we dus tot: (a < b) && c. Idemdito bij expressies als a < b + c && d.

Edit: a < b + c && d is natuurlijk iets moeilijker. We komen bij het zoeken naar het rechter deel van de "kleiner dan"-operator een hogere precedence tegen (de plus operator). Dit betekend dat we hier dus een sub-expressie beginnen.

[ Voor 5% gewijzigd door ThomasG op 13-12-2018 10:03 ]


  • dcm360
  • Registratie: December 2006
  • Niet online

dcm360

Moderator Discord

HD7767 powered

.oisyn schreef op donderdag 13 december 2018 @ 09:09:
[...]

Dat is simpelweg onjuist.

[...]
Dat is het niet. In jouw quote staat letterlijk dat het er gegarandeerd wordt dat het lijkt dat de evaluatievolgorde van links naar rechts is. In de praktijk mag er dus geschoven worden worden met de evaluatievolgorde, zolang het geobserveerde resultaat hetzelfde is. Daardoor moeten function calls van links naar rechts, en maakt het verder niet uit.

Acties:
  • +1 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 16:38

.oisyn

Moderator Devschuur®

Demotivational Speaker

ThomasG schreef op donderdag 13 december 2018 @ 09:53:
[...]
In de precedence tabel staat ook de associativity (ofwel, als er meerdere operators zijn met dezelfde precedence geeft het aan welke richting je op leest). Bij de logical and-operator is dat van links naar rechts. Eigenlijk staat er dus: a && (b && c).
Het wordt links-associatief geparst, dus (a && b) && c ;). Nou maakt dat bij && en || niet zoveel uit want ze zijn wiskundig gezien associatief: (a && b) && c == a && (b && c).

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.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 16:38

.oisyn

Moderator Devschuur®

Demotivational Speaker

dcm360 schreef op donderdag 13 december 2018 @ 10:36:
[...]

Dat is het niet. In jouw quote staat letterlijk dat het er gegarandeerd wordt dat het lijkt dat de evaluatievolgorde van links naar rechts is.
Ja, zo ken ik er ook nog wel een paar. Ga je soms ook beweren dat het niet gedefinieerd is in welke volgorde statements worden uitgevoerd? Observeerbaar gedrag is waar het om draait. Wat de compiler en cpu onder water doet is irrelevant.
In de praktijk mag er dus geschoven worden worden met de evaluatievolgorde, zolang het geobserveerde resultaat hetzelfde is. Daardoor moeten function calls van links naar rechts, en maakt het verder niet uit.
Veel te kort door de bocht. Andere expressies met side effects zijn oa assignment en increment. Maar dan zijn er ook nog dingen die exceptions kunnen gooien, bij een null reference of een deling door 0 bijvoorbeeld.

Als er geen side effects zijn is de volgorde irrelevant. Daarom hoef je maar 1 ding te onthouden: evaluatievolgorde is van links naar rechts.

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.


  • dcm360
  • Registratie: December 2006
  • Niet online

dcm360

Moderator Discord

HD7767 powered

.oisyn schreef op donderdag 13 december 2018 @ 10:47:
[...]

Ja, zo ken ik er ook nog wel een paar. Ga je soms ook beweren dat het niet gedefinieerd is in welke volgorde statements worden uitgevoerd? Observeerbaar gedrag is waar het om draait. Wat de compiler en cpu onder water doet is irrelevant.
Yeah, fine, dan compile je de code lekker niet en voer je het ook maar niet uit. En om nog maar even on-topic te reageren: kom maar op met de quote die zegt dat de Java specificatie garandeert dat statements op volgorde worden uitgevoerd. Die ga je ook niet vinden.
[...]

Veel te kort door de bocht. Andere expressies met side effects zijn oa assignment en increment. Maar dan zijn er ook nog dingen die exceptions kunnen gooien, bij een null reference of een deling door 0 bijvoorbeeld.

Als er geen side effects zijn is de volgorde irrelevant. Daarom hoef je maar 1 ding te onthouden: evaluatievolgorde is van links naar rechts.
Nouja, assignments zijn geen expressie, maar increments zijn inderdaad wel interessante. Die moeten inderdaad daadwerkelijk van links naar rechts om de illusie te behouden. Exceptions maken echter niet uit (zolang het maar blijft lijken dat de evaluatie van links naar rechts is).

Eigenlijk kan je concluderen: wil je een programma schrijven, dan mag je aannemen dat de volgorde van links naar rechts is (en statements van boven naar beneden). Als je wilt weten wat er daadwerkelijk gebeurd, kom je er achter dat die aanname eigenlijk niet klopt, maar voor het resultaat niet uitmaakt.

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 22:41
Dezelfde vraagstelling als TS heeft me eerder bezig gehouden toen ik een eigen parser wilde schrijven.

Wat mij heeft geholpen is het me verdiepen in het shunting-yard algoritme en de infix-notatie. Als je dan de precendence en associativity tabel van je programmeertaal kent, dan kan je vrij gemakkelijk je expressies herschrijven naar infix-notatie. Dat heeft mij meer inzicht gegeven in het goed doorgronden van deze materie en dan met name de volgorde waarin expressies worden geëvalueerd.

Na shunting-yard ben ik verder gegaan richting de recursive decent parser en abstract syntax tree aangezien ik het middels de shunting yard erg moeilijk vond om functies met parameters te kunnen verwerken.

Uiteindelijk heb ik zelf die parser geschreven in C en snap ik nu precies hoe dit (deel) in een programmeertaal werkt.

Wellicht kan je er wat mee :)

Sinds de 2 dagen regel reageer ik hier niet meer


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 16:38

.oisyn

Moderator Devschuur®

Demotivational Speaker

dcm360 schreef op donderdag 13 december 2018 @ 11:06:
[...]

Yeah, fine, dan compile je de code lekker niet
"Lekker niet"? kom op, we kunnen dit toch wel als volwassenen bediscussieren? :)

Ik zie niet in waarom ik de code zou moeten compileren als mijn stelling is dat het niet zo relevant is welke instructies eruit komen rollen? Jouw punt dat de loads in een andere volgorde kunnen worden gedaan is nutteloos, want je kunt niets met die informatie.
kom maar op met de quote die zegt dat de Java specificatie garandeert dat statements op volgorde worden uitgevoerd. Die ga je ook niet vinden.
Vandaar dat ik het aanhaal :). Het is zodat het lijkt alsof ze van boven naar beneden worden uitgevoerd, vanuit het oogpunt van de uitvoerende thread. Maar in een topic als deze zeg je gewoon dat statements van boven naar beneden worden uitgevoerd. Uiteraard is de stelling voor statements wel wat gevaarlijker, ivm observeerbaar gedrag van een ándere thread.
Nouja, assignments zijn geen expressie
Oh, sinds wanneer niet? Is (a = 3) + 5 geen geldige expressie in Java volgens jou?
maar increments zijn inderdaad wel interessante. Die moeten inderdaad daadwerkelijk van links naar rechts om de illusie te behouden
Nou ja, volgens jouw eigen redenatie hoeft dat helemaal niet.
Java:
1
2
int a = 1;
int b = ++a * ++a;

Kan ook worden gecompileerd worden als:
Java:
1
2
int a = 3;
int b = 6;


Om maar weer aan te geven, het is eigenlijk niet zo belangrijk welke code eruit komt rollen, zolang het eindresultaat maar matcht met de gestelde regels: evaluatievolgorde is van links naar rechts. Wat dus niet impliceert dat er ook maar iets daadwerkelijk wordt geëvalueerd.
Exceptions maken echter niet uit (zolang het maar blijft lijken dat de evaluatie van links naar rechts is).
Java:
1
a[0] + 3 / a.length;

Als a een lege int array is, geeft dit dan:
A. een IndexOutOfBoundsException
B. een ArithmeticException
C. kun je niet weten, want evaluatievolgorde is ongedefinieerd.

Je "zolang het maar blijft lijken dat de evaluatie van links naar rechts is" is natuurlijk de kern van de discussie. Bottom line is dat 3 / a.length niet mag worden geëvalueerd voor a[0], aangezien a[0] mogelijk side effects heeft (Het kan zowel een NullPointerException als een ArrayIndexOutOfBoundsException gooien).

Je stelling dat het alleen om functies gaat, wordt nu wel heel beperkt en kun je bijna beter omdraaien: het is alleen niet gedefinieerd voor simpele identifiers en operators die nooit kunnen throwen, want alleen dan valt er niets te observeren.
Eigenlijk kan je concluderen: wil je een programma schrijven, dan mag je aannemen dat de volgorde van links naar rechts is (en statements van boven naar beneden). Als je wilt weten wat er daadwerkelijk gebeurd, kom je er achter dat die aanname eigenlijk niet klopt
Maar dat is op zoveel niveaus. De IL kan niet overeenkomen met de code, de gejitte asm kan niet overeenkomen met de IL, en de uitvoering van de asm door de CPU kan vervolgens ook weer eens niet overeenkomen met de asm zelf (door out of order execution of out of order memory access). En bij al die dingen geldt dat het niet uitmaakt omdat je het gedrag toch niet kan observeren*

* Niet helemaal waar natuurlijk, zie Spectre en Meltdown ;)

[ Voor 10% gewijzigd door .oisyn op 13-12-2018 12:18 ]

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.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 16:38

.oisyn

Moderator Devschuur®

Demotivational Speaker

CurlyMo schreef op donderdag 13 december 2018 @ 11:23:
Na shunting-yard ben ik verder gegaan richting de recursive decent parser en abstract syntax tree aangezien ik het middels de shunting yard erg moeilijk vond om functies met parameters te kunnen verwerken.
Grappig, ik ben voor mijn laatste taal ook voor een gewone handgeschreven recursive decent parser gegaan ipv gebruik te maken van een parser generator, maar juist voor expressies gebruik ik gewoon een generalisatie van shunting yard :). Stuk praktischer dan al die operators hun eigen symbol te geven, en het maakt het toevoegen van een operator aan de parser ook veel simpeler (gewoon een tabelletje aanpassen en klaar).

Bij function calls (en vergelijkbaar bij vectors met de notatie [a, b, c]) haal ik trouwens niet al die losse tokens door shunting yard. Als ik een '(' tegenkom zonder een nog openstaande operator op stack en er is al een afgeronde subexpressie, dan parse ik vervolgens losse expressies gescheiden door komma's, totdat ik een ')' tegenkom. Dat geheel wordt dan een FunctionCallExpression. De expression parser zelf, waar die shunting yard dus in staat, geef je een lijstje mee met tokens waar hij moet stoppen met parsen. In dit geval dus ',' en ')'.

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.


  • dcm360
  • Registratie: December 2006
  • Niet online

dcm360

Moderator Discord

HD7767 powered

.oisyn schreef op donderdag 13 december 2018 @ 11:51:
[...]

"Lekker niet"? kom op, we kunnen dit toch wel als volwassenen bediscussieren? :)
Wie de bal kaatst... Je begon zelf met "Ja, zo ken ik er ook nog wel een paar.". Je mag zelf bedenken of je daarmee mijn reactievorm hebt uitgelokt of niet. Ik vind van wel, maar ik vind het ook niet belangrijk genoeg om er verder op door te gaan naast jou er hier even op te wijzen
Ik zie niet in waarom ik de code zou moeten compileren als mijn stelling is dat het niet zo relevant is welke instructies eruit komen rollen? Jouw punt dat de loads in een andere volgorde kunnen worden gedaan is nutteloos, want je kunt niets met die informatie.
Misschien is het voorbeeld te simplistisch, maar het doel was om aan te geven dat er met loads afgeweken mag worden van ' van links naar rechts' zonder mogelijk afwijkend resultaat, terwijl dat met function calls niet kan.
Vandaar dat ik het aanhaal :). Het is zodat het lijkt alsof ze van boven naar beneden worden uitgevoerd, vanuit het oogpunt van de uitvoerende thread. Maar in een topic als deze zeg je gewoon dat statements van boven naar beneden worden uitgevoerd.
Mja, ik houd niet zo van halve waarheden. Ik ben het er wel mee eens dat het voor de vraagstelling wel een aardig antwoord is, maar dat het een beter antwoord is als er ook wordt aangegeven dat er wat mitsen en maren aan zitten. Die extra context poogde ik te geven.
Oh, sinds wanneer niet? Is (a = 3) + 5 geen geldige expressie in Java volgens jou?
Ik zat al een beetje te bedenken waarom increments eigenlijk anders zouden zijn dan assignments... Hier zit ik inderdaad fout.
Nou ja, volgens jouw eigen redenatie hoeft dat helemaal niet.
Java:
1
2
int a = 1;
int b = ++a * ++a;

Kan ook worden gecompileerd worden als:
Java:
1
2
int a = 3;
int b = 6;
Totdat je dus iets als int b = ++a + foo(++a) * ++a;, waarbij increments maar beter wel van links naar rechts kunnen. Het hangt er een beetje van af hoe complex de expressie is of de volgorde losgelaten kan worden of niet.
Om maar weer aan te geven, het is eigenlijk niet zo belangrijk welke code eruit komt rollen, zolang het eindresultaat maar matcht met de gestelde regels: evaluatievolgorde is van links naar rechts. Wat dus niet impliceert dat er ook maar iets daadwerkelijk wordt geëvalueerd.
Zo lang de specificatie maar aangehouden wordt mag alles inderdaad :)
Java:
1
a[0] + 3 / a.length;

Als a een lege int array is, geeft dit dan:
A. een IndexOutOfBoundsException
B. een ArithmeticException
C. kun je niet weten, want evaluatievolgorde is ongedefinieerd.

Je "zolang het maar blijft lijken dat de evaluatie van links naar rechts is" is natuurlijk de kern van de discussie. Bottom line is dat 3 / a.length niet mag worden geëvalueerd voor a[0], aangezien a[0] mogelijk side effects heeft (Het kan zowel een NullPointerException als een ArrayIndexOutOfBoundsException gooien).

Je stelling dat het alleen om functies gaat, wordt nu wel heel beperkt en kun je bijna beter omdraaien: het is alleen niet gedefinieerd voor simpele identifiers en operators die nooit kunnen throwen, want alleen dan valt er niets te observeren.
Hier valt weinig aan toe te voegen, behalve dan dat ik inderdaad wat te stellig was eerder.
Maar dat is op zoveel niveaus. De IL kan niet overeenkomen met de code, de gejitte asm kan niet overeenkomen met de IL, en de uitvoering van de asm door de CPU kan vervolgens ook weer eens niet overeenkomen met de asm zelf (door out of order execution of out of order memory access). En bij al die dingen geldt dat het niet uitmaakt omdat je het gedrag toch niet kan observeren*

* Niet helemaal waar natuurlijk, zie Spectre en Meltdown ;)
Deze richting dacht ik ook nog even uit na mijn vorige post. In de daadwerkelijke uitvoering worden vaak flinke shortcuts genomen en wordt er gespeculeerd, en uiteindelijk is het resultaat nog steeds deterministisch. Zolang de shortcuts en speculaties maar vaak genoeg goed gaan levert dat een lekker snelle uitvoering van de code op, zelfs als er af en toe wat puinruimen nodig is na een verkeerde gok of shortcut.

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 22:41
.oisyn schreef op donderdag 13 december 2018 @ 12:17:
[...]
maar juist voor expressies gebruik ik gewoon een generalisatie van shunting yard :). Stuk praktischer dan al die operators hun eigen symbol te geven, en het maakt het toevoegen van een operator aan de parser ook veel simpeler (gewoon een tabelletje aanpassen en klaar).
Uiteindelijk is vrijwel elke implementatie van zo'n expressie parser een variant van shunting yard of vice versa :). Mijn abstracte implementatie bleek uiteindelijk al te bestaan onder de naam precedence climbing. Die manier was vrij simpel te implementeren in een recursive decent parser.

Sinds de 2 dagen regel reageer ik hier niet meer

Pagina: 1