Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Java: hassu muuttujien vaihto

Sivun loppuun

Mobel [16.03.2009 00:14:15]

#

static int a, b;

static void sw() {

    b = a + (0 & (a = b));
}

Miten em. myyttinen koodi toimii? Bittitason operaattoreilla tässä ilmeisesti temppuillaan, mutta jos joku viitsisi selostaa asian hieman perusteellisemmin, kohta kohdalta.

Miten ihmeessä molemmat muuttujat päivittyvät uusiin?

tgunner [16.03.2009 00:33:55]

#

Nyt näkövammainen ohjaa sokeaa, mutta veikkaisin, että suluissa oleva a = b myöntää a:lle arvon b, ja sitten b saa arvon a + 0, koska ilmeisesti 0 & (a = b) palauttaa arvon 0.

edit. Metabolixilta tuli pikkuisen hienompi selostus. :>

Metabolix [16.03.2009 00:34:40]

#

a = b sijoittaa a:han arvon b:stä ja myös palauttaa sen samaisen arvon. 0 & (a = b) jatkaa suorittamalla &-operaation (and) nollan ja tämän arvon välillä, tulos on tietenkin nolla. Yhtä hyvin voisi vaikka kertoa nollalla, mutta bittioperaatio on mahdollisesti nopeampi. Tähän nollaan lisätään a (joka ilmeisesti tarkoittaa a:n arvoa ennen rivin suoritusta) ja tulos (a + 0) sijoitetaan b:hen.

Ehkäpä rivi muuttuu apumuuttujien avulla vähemmän mystiseksi:

int Aa = a, Bb = b;
b = Aa + (0 & (a = Bb));

// Puretaan rivi vielä selkeämpiin osiin:
a = Bb;
b = Aa + (0 & a); // b = Aa + 0;

Tuollaisessa kikkailussa ei korkeamman tason kielillä ole varmasti mitään järkeä. Luultavasti näiden hyöty rajoittuu konekielellä ohjelmointiin, ja harvoinpa tuo on nykykoneilla se pullonkaula, jonka tehostaminen muuttaa koko ohjelman tasoa.

Päärynämies [16.03.2009 01:11:46]

#

Niin tuon toimintahan perustuu siihen, että tuon sijoituksen ensimmäisen a:n arvo on vielä alkuperäinen ja a:ksi sitten myöhemmin sijoitetaan b:n arvo. Jos tuota suoritettaisiinkin ensin tuolta oikealta, niin silloinhan tuo ei toimisi. En tiedä, että toimiiko tuo kaikilla kääntäjillä noin tai sanooko Javan standardi jotain siitä miten kääntäjän pitää tuo tulkita. Jos standardi ei mitään sano, niin en välttämättä lähtisi tuollaista käyttämään.

Kokeilin tuota C:llä, jotta saisin nähdäkseni sen tuottaman assemblykoodin, mutta ainakin gcc kääntää tuon minulla niin, että kummankin muuttujan arvoksi tulee 6.

Ja jos tuollaisia "mystisempiä" koodinpätkiä nyt kirjoittaa, niin olisi niitä hyvä sitten kommentoida. On sitten mukavampi itselle ja muille, kun ei tarvitse arvailla mitä ihmettä se koodi tekee. Tuossa kun vielä tuo suoritusjärjestys on olennainen asia.

Tosiaan, kuten Metabolix jo mainitsikin, niin on tuollaisesta mahdollisesti saatava hyöty hyvin minimaalinen, jos hyötyä edes on. Sitähän voi toki itse testata ja mittailla, että kuinka suuri mahdollinen ero on ja mihin suuntaan.

Tuosta mietin vielä päässä, että miten tuo kääntyy suoraan x86-assemblylle (josta löytyy toki jo xchg-käsky) ja en nyt näin yömyöhään ainakaan keksinyt miten se onnistuisi vain kahdella rekisterillä. Tarkemmin ajateltuna eipä taida olla edes mahdollista, koska tarvittava and-käsky nollaa toisen rekisterin, joten taitaa olla niin, että jokin on pakko arvo laittaa pinoon tai toiseen rekisteriin ja työtä tuleekin ehkä enemmän, kuin ilman mitään kikkailuja eli laittamalla vain toinen arvo ensin talteen, tekemällä sijoitus ja lukemalla talteen (pinoon tai toiseen rekisteriin) laitettu arvo toiseen muuttujaan.

Grez [16.03.2009 02:55:01]

#

Tuosta koodista tulee mieleen että joku on halunnut tehdä "näppärästi" swap(a,b);

Päärynämies kirjoitti:

Kokeilin tuota C:llä, jotta saisin nähdäkseni sen tuottaman assemblykoodin, mutta ainakin gcc kääntää tuon minulla niin, että kummankin muuttujan arvoksi tulee 6.

Olisi kyllä kiva tietää miten tuo on mahdollista. Siis millä vaan lähtöarvoillako? Jos vaikka a ja b ovat molemmat nolla, niin minun laskupäällä tuosta ei saa 6 mitenkään.

Päärynämies [16.03.2009 03:00:55]

#

Grez kirjoitti:

Päärynämies kirjoitti:

Kokeilin tuota C:llä, jotta saisin nähdäkseni sen tuottaman assemblykoodin, mutta ainakin gcc kääntää tuon minulla niin, että kummankin muuttujan arvoksi tulee 6.

Olisi kyllä kiva tietää miten tuo on mahdollista. Siis millä vaan lähtöarvoillako? Jos vaikka a ja b ovat molemmat nolla, niin minun laskupäällä tuosta ei saa 6 mitenkään.

Joo. Tätä se teettää kun keskellä yötä tänne kirjoittelee väsyneenä. Monesti tarkistin tuon viestini, mutta en sitten noin hölmöä virhettä sieltä tajunnut. Tosiaan käytin muuttujille ohjelmassani alkuarvoja a = 3 ja b = 6, siitä tuo tulos 6. Eli tuo saatu tulos ei mistään sinne maagisesti ilmesty, unohdin vain kertoa jotain melko oleellista. Tai no eihän noiden muuttujien arvotkaan ole kovin oleellista, oleellisinta oli se, että lopussa molemmilla oli sama arvo.

Metabolix [16.03.2009 11:56:51]

#

Päärynämies kirjoitti:

Kokeilin tuota C:llä, jotta saisin nähdäkseni sen tuottaman assemblykoodin.

C:n standardikin tosin sanoo, että kääntäjä saa laskea tietynlaiset epäselvät koodirivit haluamassaan järjestyksessä. Esimerkki hyvin tulkinnanvaraisesta rivistä:

taulu[b++] = ++b + taulu[b++];

Tämä onkin jo hyvä syy jättää käyttämättä "ovelia" koodirivejä.

Mobel [16.03.2009 12:26:58]

#

Kiitoksia vastauksista, selventivät kummasti tilannetta.
Tarkoitukseni ei ollut tuota käyttääkään. Kyseinen pätkä tuli vastaan Mureakuhan puolella ja jäin ihmettelemään sen toimintaa.

Päärynämies kirjoitti:

Jos tuota suoritettaisiinkin ensin tuolta oikealta, niin silloinhan tuo ei toimisi. En tiedä, että toimiiko tuo kaikilla kääntäjillä noin tai sanooko Javan standardi jotain siitä miten kääntäjän pitää tuo tulkita.

Mureakuhan kommenteissa annettu linkki Javan dokumentaatioon näyttäisi sanovan Javaa luettavan vasemmalta oikealle, joten kai voisi olettaa tuon vähintäänkin toimivan.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta