Kokios yra užsakymo vertinimo taisyklės „Java“?

Perskaičiau „Java“ tekstą ir gaunu šį kodą:

 int[] a = {4,4}; int b = 1; a[b] = b = 0; 

Tekste autorius nepateikė aiškaus paaiškinimo ir paskutinės eilutės poveikio: a[1] = 0;

Nesu tikras, kad suprantu: kaip buvo vertinamas?

81
23 июля '11 в 16:11 2011-07-23 16:11 „ipkiss “ nustatoma liepos 23 d. 11 val. 16:11 2011-07-23 16:11
@ 7 atsakymai

Leiskite man labai aiškiai pasakyti, nes žmonės tai nesupranta visą laiką:

Subekspresijų skaičiavimo tvarka nepriklauso nuo asociatyvumo ir prioritetų . Asociatyvumas ir prioritetas nustato, kokia tvarka operatoriai yra vykdomi, bet nenustato, kokia tvarka skaičiuojami subexpressions. Jūsų klausimas yra susijęs su subexpressions įvertinimo tvarka.

Apsvarstykite A() + B() + C() * D() . Dauginimas turi didesnį prioritetą nei pridėjimas, o pridėjimas yra kairysis asociatyvus, todėl tai yra lygiavertė (A() + B()) + (C() * D()) bet žinojimas, kad tai tik pasakys, kas atsitiks pirmiausia papildomai prie antrojo papildymo, ir kad dauginimas įvyks prieš antrąjį papildymą. Jis nesako jums, kokia tvarka A (), B (), C () ir D () bus vadinami! (Taip pat nesakoma, ar dauginimas įvyksta prieš pirmąjį papildymą, ar po jo.) Būtų visiškai įmanoma laikytis pirmenybės ir asociatyvumo taisyklių:

 d = D() // these four computations can happen in any order b = B() c = C() a = A() sum = a + b // these two computations can happen in any order product = c * d result = sum + product // this has to happen last 

Čia laikomasi visų prioritetų ir asociatyvumo taisyklių - pirmasis papildymas įvyksta prieš antrąjį papildymą ir dauginimas įvyksta prieš antrąjį papildymą. Akivaizdu, kad galime skambinti į A (), B (), C () ir D () bet kokia tvarka ir tuo pat metu laikytis pirmenybės ir asociatyvumo taisyklių!

Mums reikia taisyklės, nesusijusios su prioritetų taisyklėmis ir asociatyvumu, kad paaiškintume, kokia tvarka subekspresijos yra vertinamos. Atitinkama „Java“ (ir С #) taisyklė: „subexpressions skaičiuojami iš kairės į dešinę“. Kadangi A () rodomas C () kairėje, pirmiausia apskaičiuojamas A (), nepaisant to, kad C () dalyvauja dauginime, o A () dalyvauja tik papildomai.

Taigi dabar turite pakankamai informacijos, kad galėtumėte atsakyti į jūsų klausimą. a[b] = b = 0 , asociatyvumo taisyklės sako, kad tai yra a[b] = (b = 0); bet tai nereiškia, kad b=0 prasideda pirmiausia! Prioritetinės taisyklės sako, kad indeksavimas turi didesnį prioritetą nei priskyrimas, tačiau tai nereiškia, kad indeksavimo priemonė veikia prieš dešiniojo priskyrimo .

(UPDATE: ankstesnėje šio atsakymo versijoje kitame skyriuje buvo keletas nedidelių ir beveik nesvarbių praleidimų, kuriuos pataisiau. Taip pat parašiau dienoraščio straipsnį, kuriame aprašoma, kodėl šios taisyklės yra pagrįstos „Java“ ir „C #“ čia: https://ericlippert.com / 2019/01/18 / indeksavimo klaidų atvejai / )

Prioritetas ir asociatyvumas mums tik sako, kad nulis b priskyrimas turi įvykti prieš priskyrimą a[b] , nes nulio priskyrimas apskaičiuoja indeksavimo operacijai priskirtą vertę. Vien tik prioritetas ir asociatyvumas nieko nesako apie tai, ar a[b] yra vertinamas prieš arba po b=0 .

Vėlgi, tai yra tokia pati kaip: A()[B()] = C() - visi žinome, kad indeksavimas turi būti atliktas prieš paskyrimą. Mes nežinome, ar A (), B () ar C () yra atliekami pirmiausia pagal prioritetą ir asociatyvumą. Mums reikia kitos taisyklės, kad tai pasakytume.

Taisyklė vėl, „kai jūs turite galimybę pasirinkti, ką daryti pirmiausia, visada eikite iš kairės į dešinę“. Tačiau šiame konkrečiame scenarijuje yra įdomus raukšlėjimas. Ar išmestos išimties šalutinis poveikis atsiranda dėl nulinės kolekcijos ar indekso, esančio už leistino diapazono ribų, laikomo dalies tikslo kairiosios dalies arba paties tikslo skaičiavimo dalimi? Java pasirenka pastarąjį. (Žinoma, šis skirtumas skiriasi tik tuo atveju, jei kodas jau yra neteisingas, nes teisingas kodas nulemia nulį ir neperduoda blogo indekso.)

Taigi, kas atsitiks?

  • A a[b] yra kairėje nuo b=0 , todėl pirmą kartą pradedama a[b] , kuri veda prie a[1] . Tačiau šio indeksavimo operacijos patvirtinimas atidedamas.
  • Tada atsiranda b=0 .
  • Tada yra patikrinimas, ar a yra galiojantis, o a[1] yra diapazone
  • Vertės a[1] priskyrimas įvyksta paskutinis.

Taigi, nors šiuo konkrečiu atveju yra keletas subtilybių, į kurias reikėtų atsižvelgti tais retais atvejais, kai klaidos neturėtų būti pirmą kartą įvestos teisingame kode, apskritai jūs galite pasakyti: kairėje atsitinka dalykai, esantys prieš dešinėje esančius dalykus . Tai yra taisyklė, kurią ieškote. Kalbėjimas apie darbo stažą ir asociatyvumą yra painus ir netinkamas.

Žmonės nuolat klysta, net ir tuos, kuriems reikia geriau žinoti. Redagavau per daug knygų apie programavimą, kuriose taisyklės buvo neteisingai suformuluotos, todėl nenuostabu, kad daugelis žmonių turi visiškai klaidingą nuomonę apie ryšį tarp prioriteto / asociatyvumo ir vertinimo tvarkos, ty iš tikrųjų nėra tokių santykių; jie yra nepriklausomi.

Jei ši tema jus domina, žr. „Mano straipsniai šia tema“ toliau skaitykite:

http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/

Jie yra apie C #, bet dauguma šių dalykų vienodai gerai taikomi „Java“.

165
23 июля '11 в 18:34 2011-07-23 18:34 atsakymą pateikė Eric Lippert, liepos 23 d., 11 d., 18:34, 2011-07-23 18:34

Ericas Lippertas profesionaliai atsako, bet nėra labai naudingas, nes tai yra kitokia kalba. Tai Java, kur Java kalbos specifikacija yra galutinis semantikos aprašymas. Konkrečiai, § 15.26.1 yra reikšmingas, nes jame aprašoma operatoriaus vertinimo tvarka = (visi žinome, kad tai teisinga asociacija, tiesa?). Šiek tiek redaguodami šią problemą rūpinsime:

Jei kairioji operandų išraiška yra masyvo prieigos išraiška ( §15.13 ), reikia atlikti keletą veiksmų:

  • Pirma, įvertinama kairiojo operando prieigos matricos masyvo nuoroda. Jei šis vertinimas staiga baigsis, užduoties išraiška staiga užbaigiama dėl tos pačios priežasties; indekso subexpression (iš prieigos išraiška į kairįjį operandinį elementą) ir dešinįjį operandą nėra vertinami ir jokio priskyrimo nėra.
  • Priešingu atveju, įvertinamas kairiosios išraiškos indekso subexpressionas, pasiekiantis operando elementą. Jei šis vertinimas staiga baigsis, priskyrimo išraiška staiga užbaigiama dėl tos pačios priežasties, o dešinysis operandas nėra vertinamas, o priskyrimas nevyksta.
  • Priešingu atveju vertinamas tinkamas operandas. Jei šis vertinimas staiga baigsis, priskyrimo išraiška staiga užbaigiama dėl tos pačios priežasties ir nenustatoma jokio priskyrimo.

[... Toliau aprašoma tikroji užduoties reikšmė, kurią galime ignoruoti čia ...]

Trumpai tariant, „Java“ turi labai griežtai apibrėžtą vertinimo tvarką , kuri yra beveik tiksliai iš kairės į dešinę argumentuose bet kuriam operatoriui ar metodui. Array užduotys yra vienas iš sunkiausių atvejų, bet net ir ten vis dar yra L2R. (JLS rekomenduoja ne parašyti kodo, kuriam reikalingi tokie sudėtingi semantiniai suvaržymai, ir taip. Taip, galite gauti daugiau nei pakankamai problemų tik vienam paskyrimui vienam operatoriui!)

C ir C + + tikrai skiriasi nuo „Java“ šioje srityje: jų kalbos apibrėžimai įvertinimo tvarką palieka neapibrėžtai, kad būtų galima geriau optimizuoti. C # yra panašus į „Java“, bet aš nežinau jo literatūros pakankamai gerai, kad galėčiau nurodyti oficialų apibrėžimą. (Tai iš tikrųjų priklauso nuo kalbos, nors Ruby yra griežtai L2R, kaip ir Tcl - nors ji neturi priskyrimo operatoriaus dėl priežasčių, dėl kurių čia nėra reikšmės, ir Python L2R, bet R2L, atsižvelgiant į paskirties vietą , kurią man atrodo keista, bet ten tu buvai. .)

32
24 июля '11 в 1:57 2011-07-24 01:57 atsakymas pateikiamas Donal Fellows, liepos 24 d., 11 d., 1:57 2011-07-24 01:57
 a[b] = b = 0; 

1) masyvo indekso operatorius turi didesnį prioritetą nei priskyrimo operatorius (žr. Šį atsakymą ):

 (a[b]) = b = 0; 

2) Pagal 15.26. JLS priskyrimo pareiškimai

Yra 12 priskyrimo operatorių; jie visi yra sintaksiškai tinkami asociatyvūs (jie sugrupuoti iš dešinės į kairę). Taigi a = b = c reiškia a = (b = c), kuri priskiria c vertę b ir tada priskiria b a vertę.

 (a[b]) = (b=0); 

3) Pagal 15.7. JLS reitingų tvarka

„Java“ programavimo kalba užtikrina, kad operatorių operandai bus vertinami konkrečia vertinimo tvarka, ty iš kairės į dešinę.

ir

Kairysis dvejetainio operatoriaus operandas yra visiškai įvertintas prieš vertinant bet kurią dešiniojo operando dalį.

Taigi:

a) (a[b]) pirmą kartą įvertintas a[1]

b), tada (b=0) apskaičiuojama kaip 0

c) (a[1] = 0) yra paskutinis

5
07 окт. atsakė bup Oct 07 2015-10-07 13:11 '15, 13:11 2015-10-07 13:11

Jūsų kodas atitinka:

 int[] a = {4,4}; int b = 1; c = b; b = 0; a[c] = b; 

kuris paaiškina rezultatą.

1
23 июля '11 в 16:18 2011-07-23 16:18 atsakymą pateikė Jérôme Verstrynge , liepos 23 d., 11 d., 16:18, 2011-07-23 16:18

Čia yra dar vienas pavyzdys, kaip apibūdinti prioritetinę tvarką „Java“.

Apsvarstykite šį pavyzdžio kodą:

 class A{ public static void main(String[] args){ int x = 20; x =x+(x=5); System.out.println(x); }} 

Klausimas: Kas yra x išvestis? Pabandykite išspręsti šią problemą prieš tęsdami atsakymą.

Atsakymas:

1 veiksmas: kompiliatorius skiria 20 kintamajam x. Tai svarbu - visi kintamieji yra apibrėžti pirmiausia, ty

 int x = 20; 

2 veiksmas. Kompiliatorius taiko pirmenybės tvarka, ty skliausteliuose:

 (x=5); 

Dabar mes turime:

 x =20+(x=5); 

Rezultatas:

 x =20+5; 

Todėl produkcija yra 25:

 System.out.println(x); 

Išvados:

Jūs turite būti toks:

1 Visi kintamieji pirmiausia apibrėžiami išraiškose.

2 Jūs žinote prioritetų tvarkymo ir asociatyvumo operatorių.

0
16 нояб. Mark Burleigh atsakymas lapkričio 16 d 2016-11-16 13:48 '16, 13:48 pm 2016-11-16 13:48

Apsvarstykite kitą išsamesnį pavyzdį.

Kaip bendroji taisyklė:

Geriausia, jei, sprendžiant šiuos klausimus, būtų galima skaityti prioritetų ir asociacijų taisyklių lentelę. http://introcs.cs.princeton.edu/java/11precedence/

Čia yra geras pavyzdys:

 System.out.println(3+100/10*2-13); 

Klausimas: Kokia pirmiau nurodytos eilutės išvestis?

Atsakymas: taikyti prioritetines ir asociatyvumo taisykles

1 žingsnis: Pagal prioritetines taisykles: / ir * operatoriai turi pirmenybę + - operatoriams. Todėl šios lygties įgyvendinimo pradžios taškas bus susiaurintas iki:

 100/10*2 

2 žingsnis: Pagal taisykles ir prioritetą: / ir * yra vienodos pirmenybės.

Operatoriai / ir * yra prioritetiniai, todėl turime ieškoti asociacijų tarp šių operatorių.

Pagal šių dviejų konkrečių operatorių ASOCIATIVITETO TAISYKLES, pradedame lygties iš LEFT TO RIGHT įgyvendinimą, ty 100/10 atliekamas pirmiausia:

 100/10*2 =100/10 =10*2 =20 

3 žingsnis: lygtis dabar vykdoma:

 =3+20-13 

Pagal taisykles ir prioritetą: + ir - pirmenybė teikiama vienodai.

Dabar turime ieškoti asociacijų tarp + ir - operatorių. Remiantis šių dviejų konkrečių operatorių asociatyvumu, pradedame lygties įgyvendinimą iš kairės į dešinę, t.y. 3 + 20 vykdoma pirmiausia:

 =3+20 =23 =23-13 =10 

10 - teisinga išvestis rengiant

Vėlgi, svarbu, kad su jumis būtų atsižvelgiama į prioritetinių ir asociacijų taisyklių lentelę, pvz., Sprendžiant šiuos klausimus. http://introcs.cs.princeton.edu/java/11precedence/

0
16 нояб. Mark Burleigh atsakymas lapkričio 16 d 2016-11-16 20:56 '16 at 8:56 pm 2016-11-16 20:56
 public class TestClass{   public static void main(String args[] ){  int i = 0 ;  int[] iA = {10, 20} ;  iA[i] = i = 30 ; System.out.println(""+ iA[ 0 ] + " " + iA[ 1 ] + "  "+i) ;} } 

Jis spausdins 30 20 30

Pareiškimas iA [i] = i = 30; bus tvarkomi taip:

iA [i] = i = 30; => iA [0] = i = 30; => i = 30; iA [0] = i; => iA [0] = 30;

Štai ką JLS sako apie tai:

1 Pirmiausia įvertinkite operatoriaus kairę ranką.
2 Prieš operaciją įvertinkite operandus.
3 Svarstyklių ir prioritetų atitikties įvertinimas
4 argumentų sąrašai vertinami kairėn dešinėn

Masyvams: pirma, matavimo išraiškos vertinamos iš kairės į dešinę. Jei bet koks išraiškos vertinimas staiga baigsis, jo dešinėje esančios išraiškos nėra vertinamos.

-1
03 февр. Atsakymą pateikė Kevin STS 03 vasaris. 2017-02-03 19:33 '17, 7:33 pm 2017-02-03 19:33

Kiti klausimai apie „ žymes arba Užduoti klausimą