Kierto (ohjelmointi)

Kokeneet kirjoittajat eivät ole vielä tarkistaneet sivun nykyistä versiota, ja se voi poiketa merkittävästi 7. helmikuuta 2022 tarkistetusta versiosta . tarkastukset vaativat 3 muokkausta .

Silmukka  on eräänlainen ohjausrakenne korkean tason ohjelmointikielissä , joka on suunniteltu järjestämään komentosarjan toistuva suorittaminen . Jaksoksi voidaan myös kutsua mitä tahansa toistuvasti suoritettua käskysarjaa, joka on järjestetty millä tahansa tavalla (esimerkiksi ehdollisen hypyn avulla ).

Määritelmät

Toistuvasti suoritettavaksi tarkoitettua käskysarjaa kutsutaan silmukan rungoksi . Silmukan rungon yksittäistä suoritusta kutsutaan iteraatioksi . Lauseketta , joka määrittää, suoritetaanko iteraatio uudelleen vai päättyykö silmukka, kutsutaan silmukan poistumisehdoksi (tai jatkuvuuden ehdoksi , riippuen siitä, kuinka sen totuus tulkitaan - merkkinä lopettamistarpeesta tai jatka silmukkaa). Muuttujaa , joka tallentaa nykyisen iteraationumeron , kutsutaan silmukan iteraatiolaskuriksi tai yksinkertaisesti silmukkalaskuriksi . Silmukka ei välttämättä sisällä laskuria, laskurin ei tarvitse olla yksi - silmukasta poistumisen ehto voi riippua useista silmukassa muuttuneista muuttujista tai sen voi määrittää ulkoiset olosuhteet (esim. aika), jälkimmäisessä tapauksessa laskuria ei ehkä tarvita ollenkaan.

Minkä tahansa silmukan suoritus sisältää silmukkamuuttujien alustavan alustuksen, poistumisehdon tarkistamisen, silmukan rungon suorittamisen ja silmukkamuuttujan päivittämisen jokaisessa iteraatiossa. Lisäksi useimmat ohjelmointikielet tarjoavat keinoja silmukan varhaiseen hallintaan, esimerkiksi silmukan lopetuskäskyt, eli poistuminen silmukasta poistumisehdon totuudesta riippumatta ( C -kielellä  - break) ja iteroinnin ohitusoperaattorit ( C-kielellä - continue).

Syklityypit

Ehdottomat silmukat

Joskus ohjelmat käyttävät silmukoita, joista poistumista ei ohjelman logiikka tarjoa. Tällaisia ​​syklejä kutsutaan ehdottomaksi tai äärettömäksi. Epätyypillisyytensä vuoksi ohjelmointikielet eivät tarjoa erityisiä syntaktisia keinoja äärettömien silmukoiden luomiseen, joten tällaiset silmukat luodaan rakenteilla, jotka on suunniteltu luomaan tavallisia (tai ehdollisia ) silmukoita. Äärettömän toiston varmistamiseksi tällaisessa silmukassa ehtotarkistus joko puuttuu (jos syntaksi sallii, kuten esimerkiksi AdaLOOP ... END LOOP - kielisilmukassa ), tai se korvataan vakioarvolla ( Pascalissa ). C - kieli käyttää silmukkaa , jossa on tyhjiä osia, tai silmukkaa . while true do ...for(;;)while (1)

Silmukka, jossa edellytys

Ehdolla varustettu silmukka on silmukka, joka suoritetaan, kun jokin ennen sen alkua määritetty ehto on tosi. Tämä ehto tarkistetaan ennen silmukan rungon suorittamista, joten runkoa ei välttämättä suoriteta edes kerran (jos ehto on epätosi alusta alkaen). Useimmissa proseduuriohjelmointikielissä se toteutetaan while -lauseella , joten sen toinen nimi on while-silmukka. Pascalissa silmukka, jolla on ennakkoehto, näyttää tältä:

while < ehto > aloita < silmukan runko > loppu ; _

C - kielellä :

while ( < ehto > ) { < loop body > }

Silmukka jälkiehdon kanssa

Jälkiehdon sisältävä silmukka on silmukka, jossa ehto tarkistetaan silmukan rungon suorittamisen jälkeen . Tästä seuraa, että ruumis teloitetaan aina vähintään kerran. Pascalissa tämän silmukan toteuttaa operaattori repeat..until ; C - do…while.

Pascalissa silmukka, jossa on jälkiehto, näyttää tältä:

toista < loop body > kunnes < exit condition >

C-kielellä:

do { < loop body > } while ( < silmukan jatkoehto > ) _

Silmukkaehdon tulkinnassa jälkiehdon kanssa on eroja eri kielillä. Pascalissa ja siitä syntyneissä kielissä tällaisen syklin ehtoa käsitellään poistumisehtona (sykli päättyy, kun ehto on tosi, venäjän terminologiassa tällaisia ​​jaksoja kutsutaan myös "sykliksi asti") ja C ja sen jälkeläiset - jatkoehtona (sykli päättyy, kun ehto on epätosi, tällaisia ​​silmukoita kutsutaan joskus while-silmukaksi).

Pyöräile keskeltä ulos

Keskimmäinen poistumissilmukka on ehdollisen silmukan yleisin muoto. Syntaktisesti tällainen sykli muodostetaan käyttämällä kolmea rakennetta: syklin alkua, syklin loppua ja syklistä poistumiskomentoa. Alkukonstruktio merkitsee ohjelman pistettä, josta silmukan runko alkaa, loppurakenne merkitsee pistettä, jossa runko päättyy. Rungon sisällä tulee olla silmukasta poistumiskomento, jonka suorittamisen jälkeen silmukka päättyy ja ohjaus siirtyy silmukan päätykonstruktia seuraavalle operaattorille. Luonnollisesti, jotta silmukka voidaan suorittaa useammin kuin kerran, poistumiskomentoa ei pitäisi kutsua ehdoitta, vaan vain silloin, kun silmukasta poistumisen ehto täyttyy.

Perimmäinen ero tämän tyyppisten silmukoiden ja edellä käsiteltyjen välillä on, että se osa silmukan rungosta, joka sijaitsee silmukan alun jälkeen ja ennen poistumiskomentoa, suoritetaan aina (vaikka silmukan poistumisehto olisi tosi ensimmäisessä iteraatiossa ), ja sitä osaa silmukan rungosta, joka sijaitsee exit-komennon jälkeen, ei suoriteta viimeisellä iteraatiolla.

On helppo nähdä, että keskimmäisellä poistumissilmukalla voit helposti mallintaa sekä ennakkoehtosilmukan (sijoittamalla exit-lause silmukan rungon alkuun) että jälkiehtosilmukan (asettamalla exit-käsky silmukan loppuun runko).

Jotkut ohjelmointikielet sisältävät erityisiä rakenteita silmukan järjestämiseksi, jossa on uloskäynti keskeltä. Joten Adan kielessä tähän käytetään konstruktiota LOOP ... END LOOPja exit-komentoa EXITtai EXIT WHEN:

LOOP ... Silmukan runko - osa EXIT WHEN < poistumisehto > ; ... Loop runko - osa IF < exit condition > THEN EXIT ; LOPPU ; ... Osa END LOOP : n rungosta :

Tässä silmukan sisällä voi olla mikä tahansa määrä molempien tyyppisten poistumiskomentoja. Itse poistumiskomennot eivät pohjimmiltaan eroa toisistaan, niitä käytetään yleensä EXIT WHEN, kun vain poistumisehto tarkistetaan, mutta yksinkertaisesti EXIT silmukasta poistuttaessa jossakin monimutkaisen ehdollisen lauseen muunnelmista.

Kielessä, jossa tällaisia ​​rakenteita ei ole, silmukka, jossa on poistuminen keskeltä, voidaan mallintaa käyttämällä mitä tahansa ehdollista silmukkaa ja silmukan aikaisen poistumisen operaattoria (kuten breakC:ssä, exit Turbo Pascalissa jne.) tai ehdoton operaattorin siirtyminen goto .

Silmukka laskurin kanssa (tai silmukka for)

Silmukka, jossa on laskuri, on silmukka, jossa jokin muuttuja muuttaa arvonsa annetusta alkuarvosta lopulliseksi arvoksi jollain askeleella, ja jokaiselle tämän muuttujan arvolle silmukan runko suoritetaan kerran. Useimmissa proseduuriohjelmointikielissä se toteutetaan käskyllä for, joka määrittää laskurin (ns. "silmukkamuuttuja"), vaaditun kulkumäärän (tai laskurin raja-arvon) ja mahdollisesti vaiheen, jossa laskuri muutoksia. Esimerkiksi Oberon-2- kielellä tällainen sykli näyttää tältä:

FOR v := b TO e BY s DO ... silmukan runko LOPPU

tässä v on laskuri, b on laskurin alkuarvo, e on laskurin raja-arvo, s on askel).

Kysymys muuttujan arvosta sellaisen silmukan lopussa, jossa tätä muuttujaa käytettiin laskurina, on epäselvä. Esimerkiksi, jos Pascal-ohjelma kohtaa lomakkeen konstruktion:

i := 100 ; for i : = 0 - 9 aloita ... silmukan rungon loppu ; k := i ;

herää kysymys: mikä arvo lopulta annetaan muuttujalle k : 9, 10, 100, kenties jokin muu? Entä jos kierto loppuu ennenaikaisesti? Vastaukset riippuvat siitä, kasvatetaanko laskurin arvoa viimeisen iteraation jälkeen ja muuttaako kääntäjä tätä arvoa lisäksi. Vielä yksi kysymys: mitä tapahtuu, jos laskurille annetaan nimenomaisesti uusi arvo silmukan sisällä? Eri ohjelmointikielet käsittelevät näitä ongelmia eri tavoin. Joissakin laskurin käyttäytyminen on selkeästi säänneltyä. Toisissa, esimerkiksi samassa Pascalissa, kielistandardi ei määrittele laskurin lopullista arvoa eikä sen eksplisiittisen muutoksen seurauksia silmukassa, mutta se ei suosittele laskurin vaihtamista eksplisiittisesti ja sen käyttöä lopussa. silmukasta ilman uudelleenalustusta. Pascal-ohjelma, joka jättää tämän suosituksen huomioimatta, voi tuottaa erilaisia ​​tuloksia, kun se suoritetaan eri järjestelmissä ja eri kääntäjiä käyttäen.

Ongelma on ratkaistu radikaalisti Ada- ja Kotlin -kielissä : laskurin katsotaan kuvattuna silmukan otsikossa, eikä sitä yksinkertaisesti ole olemassa sen ulkopuolella. Vaikka laskurin nimi olisikin jo käytössä ohjelmassa, silmukan sisällä käytetään laskurina erillistä muuttujaa. On kiellettyä nimenomaan antaa mitään arvoja laskuriin, sitä voidaan muuttaa vain silmukkaoperaattorin sisäisellä mekanismilla.

Tämän seurauksena Adan rakentaminen:

i := 100 ; for i in ( 0. . 9 ) silmukka ... silmukka rungon loppu silmukka ; k := i ;

Ja Kotlinissa:

val i = 100 ; for ( i in 0. . 9 ){ ... silmukan runko } val k = i ;

Ulkoisesti samanlainen kuin yllä oleva Pascal-silmukka, se tulkitaan yksiselitteisesti: muuttujalle k annetaan arvo 100, koska tämän silmukan ulkopuolella käytetyllä muuttujalla i ei ole mitään tekemistä laskurin i kanssa , joka luodaan ja muutetaan silmukan sisällä . Tällainen laskurin eristäminen on kätevää ja turvallista: sille ei tarvita erillistä kuvausta, ja silmukan ulkopuolisten muuttujien vahingossa tapahtuvaan tuhoutumiseen liittyvien satunnaisvirheiden todennäköisyys on minimaalinen. Jos ohjelmoijan on sisällytettävä valmiiseen koodiin sykli laskurilla, hän ei saa tarkistaa, onko laskuriksi valitsemansa nimeä omaavaa muuttujaa, eikä lisätä uuden laskurin kuvausta laskurin otsikkoon. vastaavaa menettelyä, älä yritä käyttää jotakin saatavilla olevista, vaan tässä "vapaiden" laskurien hetkessä. Hän yksinkertaisesti kirjoittaa silmukan muuttujalaskurilla, jonka nimi on hänelle sopiva, ja voi olla varma, ettei nimitörmäystä tapahdu.

Laskurilla varustettu silmukka voidaan aina kirjoittaa ehdollisena silmukana, jonka alkua ennen laskurille annetaan alkuarvo ja poistumisehtona on, että laskuri saavuttaa loppuarvon; samalla silmukan runkoon lisätään operaattori laskurin muuttamiseksi tietyllä askeleella. Laskurilla varustetun syklin erikoisoperaattoreita voidaan kuitenkin kääntää tehokkaammin, koska tällaisen syklin formalisoitu muoto mahdollistaa erityisten prosessoriohjeiden käytön syklien järjestämiseen.

Niklaus Wirth kutsui aikoinaan silmukkaa laskurilla "marginaaliksi" väittäen, että tällainen rakenne on tarpeeton ja se tulisi jättää ohjelmointikielten syntaksista pois ei-järjestelmäksi. Tämän näkemyksen mukaan Oberon -ohjelmointikielessä ei ollut laskuria sisältävää sykliä . Wirthin ja Mössenböckin Oberonin kehitystyössä luomassa Oberon-2-kielessä laskurin sisältävä silmukka ilmestyi kuitenkinFOR jälleen käytännön käytettävyyden vuoksi [1] .

Joissakin kielissä, kuten C ja muissa siitä johdetuissa kielissä, silmukka on laskurin syntaktisesta muodostafor huolimatta itse asiassa silmukka, jolla on ehto. Eli C:ssä silmukkakonstruktio:

for ( i = 0 ; i < 10 ; ++ i ) { ... silmukan runko }

edustaa itse asiassa toista konstruktion merkintämuotoa [2] :

i = 0_ _ kun ( minä < 10 ) { ... loop body ++ i ; }

Toisin sanoen konstruktiossa forkirjoitetaan ensin mielivaltainen syklin alustuslause, sitten jatkoehto ja lopuksi jokin operaatio, joka suoritetaan jokaisen syklin rungon jälkeen (tämän ei tarvitse olla laskurin muutos se voi olla osoittimen muokkaaminen tai jokin täysin ulkopuolinen toiminto). Tällaisilla kielillä yllä kuvattu ongelma ratkaistaan ​​hyvin yksinkertaisesti: laskurimuuttuja käyttäytyy täysin ennustettavasti ja silmukan lopussa säilyttää viimeisen arvonsa.

Yhteinen sykli

Toinen silmukan muunnelma on silmukka, joka määrittää jonkin toiminnon suorittamisen tietyn joukon objekteille ilman, että nimenomaisesti määritellään järjestystä, jossa nämä objektit luetellaan. Tällaisia ​​jaksoja kutsutaan liitoksiksi (samoin kuin keräyssykleiksi , näkymäsykleiksi ) ja ne edustavat muotoa: "Suorita operaatio X kaikille joukon M elementeille". Liitossilmukka ei teoriassa määritä millään tavalla, missä järjestyksessä operaatiota sovelletaan joukon elementteihin, vaikka tietyt ohjelmointikielet voivat tietysti määrittää tietyn järjestyksen elementtien iterointiin. Mielivaltaisuus mahdollistaa syklin suorituskyvyn optimoinnin järjestämällä pääsyn ei ohjelmoijan järjestyksessä, vaan edullisimmassa järjestyksessä. Useiden operaatioiden rinnakkaissuoritusmahdollisuuden ansiosta on mahdollista jopa rinnakkaista yhteisen syklin suoritus, kun sama operaatio suoritetaan samanaikaisesti eri laskentamoduuleilla eri kohteille, ohjelman pysyessä loogisesti johdonmukaisena.

Joint-silmukat ovat saatavilla joillakin ohjelmointikielillä ( C# , Eiffel , Java , JavaScript , Perl , Python , PHP , LISP , Tcl jne.) - niiden avulla voit kiertää tietyn objektikokoelman kaikki elementit . Tällaisen silmukan määrittelyssä sinun tarvitsee vain määrittää objektikokoelma ja muuttuja, joille silmukan rungossa määritetään parhaillaan käsiteltävän objektin arvo (tai viittaus siihen). Eri ohjelmointikielissä operaattorin syntaksi on erilainen:

C++ :

for ( type & item : set ) //tuettu alkaen C++11 { //käytä tuotetta }

C# :

foreach ( kirjoita kohde joukkoon ) { //käyttäen tuotetta }

Delphi :

kohteelle [ 1 .. 100 ] do begin //Using item (Tämä koodi on testattu Delphi 2010 ) end ;

Perl (tiukka ensimmäisestä viimeiseen järjestys):

foreach ( @set ) { #use $_ } # or for ( @set ) { #use $_ } # or foreach $item ( @set ) { #use $item }

eiffel :

poikki asetettu kohdistimen silmukaksi -- käytä cursor.item end

Java :

for ( type item : set ) { //käyttäen kohdetta }

JavaScript :

for ( txtProperty in objObject ) { /* käyttö: objObject [txtProperty] */ }

PHP :

foreach ( $arr kuten $item ) { /* käytä $tuote*/ } //tai foreach ( $arr $keynä = > $ arvo ) { /* käytä $avaimen indeksiarvoja ja $arvoa*/ } //tai foreach ( $arr as & $item ) { /*käytä $item viitteellä*/ }

Visual Basic . netto :

Jokaiselle tuotteelle Tyypin mukaan Aseta ' käytä tuotetta Seuraava kohde

Windows PowerShell :

foreach ($item $setissä) { # toimintoa $item }

tai

$set | ForEach-Object { # toimintoa $_ }

Python

kohteelle iterator_instance : # käytä kohdetta

rubiini

iterator_instance . jokainen tekee | tuote | # käytä tuotteen loppua

Varhainen poistuminen ja iteroinnin ohittaminen

Monilla ohjelmointikielillä, joiden syntaksissa on syklisiä rakenteita, on myös erityisiä komentoja, joiden avulla voit rikkoa näiden rakenteiden toimintajärjestystä: komento poistua silmukasta aikaisin ja komento ohittaa iteraatio.

Aikainen poistuminen syklistä

Early exit -komentoa käytetään, kun on tarpeen keskeyttää sellaisen silmukan suoritus, jossa poistumisehtoa ei ole vielä saavutettu. Näin tapahtuu esimerkiksi silloin, kun silmukan rungon suorittamisen aikana havaitaan virhe, jonka jälkeen silmukan jatkotyöskentely ei ole järkevää.

Varhaisen poistumisen käskyä kutsutaan yleensä nimellä EXITtai break, ja sen vaikutus on samanlainen kuin ehdottoman haaran ( goto) käsky välittömästi sen silmukan jälkeisessä käskyssä, jossa tämä käsky sijaitsee. Joten C-kielessä seuraavat kaksi silmukkaa toimivat täsmälleen samalla tavalla:

// Break-lauseen käyttö while ( < ehto > ) { ... operaattorit if ( < error > ) break ; ... operaattorit } ... ohjelman jatkoa // Samanlainen fragmentti ilman taukoa while ( < ehto > ) { ... operaattorit if ( < error > ) goto break_label ; ... operaattorit } break_label : ... ohjelman jatkoa

Molemmissa tapauksissa, jos <error>-ehto täyttyy silmukan rungossa, siirrytään lauseisiin, jotka on nimetty "ohjelman jatkoksi". Siten silmukan aikaisen poistumisen operaattori itse asiassa yksinkertaisesti peittää ehdottoman haaran, mutta break on parempi kuin goto, koska break käyttäytyminen on selkeästi kielen määrittelemä, mahdollisesti vähemmän vaarallinen (esim. mahdollisuus tehdä virhe etiketin sijainnissa tai nimessä). Lisäksi selkeä varhainen poistuminen silmukasta ei riko strukturoidun ohjelmoinnin periaatteita.

Tavallinen varhainen poistumiskäsky päättää silmukan, jossa se suoraan sijaitsee. Useissa ohjelmointikielissä tämän operaattorin toimintoja on laajennettu, sen avulla voit poistua useista sisäkkäisistä silmukoista (katso alla). Tällaisissa tapauksissa silmukka, josta poistutaan, on merkitty etiketillä, ja etiketti määritetään aikaisen poistumisen käskyssä.

Ohita iteraatio

Tätä operaattoria käytetään, kun nykyisessä silmukan iteraatiossa on välttämätöntä ohittaa kaikki komennot silmukan rungon loppuun asti. Tässä tapauksessa itse silmukkaa ei saa katkaista, vaan jatkamis- tai poistumisehdot tulee laskea tavalliseen tapaan.

C:ssä ja sen jälkeläisissä iteroinnin ohituskomento on lauseke continuesilmukkakonstruktissa. Tämän operaattorin toiminta on samanlainen kuin ehdoton hyppy silmukan rungon sisällä olevalle riville sen viimeisen komennon jälkeen. Esimerkiksi C-koodi, joka löytää taulukon elementtien summan ja taulukon kaikkien positiivisten elementtien summan, voi näyttää tältä:

int arr [ ARRSIZE ]; ... // Arr-taulukon kaikkien ja vain positiivisten elementtien yhteenlaskeminen erikseen // jatkaa. int summa_all = 0 ; int summa_pos = 0 ; for ( int i = 0 ; i < ARRSIZE ; ++ i ) { summa_all += arr [ i ]; if ( arr [ i ] <= 0 ) jatka ; summa_pos += arr [ i ]; } // Samanlainen koodi c goto int sum_all = 0 ; int summa_pos = 0 ; for ( int i = 0 ; i < ARRSIZE ; ++ i ) { summa_all += arr [ i ]; if ( arr [ i ] <= 0 ) goto cont_label ; summa_pos += arr [ i ]; cont_label : }

Toinen katkelma osoittaa selvästi, kuinka se toimii continue: se yksinkertaisesti siirtää silmukan rungon viimeisen komennon hallinnan ohittaen summauskomennon suorittamisen, jos taulukon seuraava elementti ei täytä ehtoa. Siten sum_pos kerää vain taulukon positiivisten elementtien summan.

välttämättömyys

Rakenteellisen ohjelmoinnin näkökulmasta silmukan poistumis- ja iteroinnin ohituskomennot ovat redundantteja, koska niiden toiminta on helposti mallinnettavissa puhtaasti rakenteellisilla keinoilla. Lisäksi useiden ohjelmointiteoreetikkojen (erityisesti Edsger Dijkstra) mukaan se tosiasia, että ohjelmassa käytetään ei-rakenteellisia keinoja, olipa kyseessä sitten klassinen ehdoton hyppy tai mikä tahansa sen erikoistunut muoto, kuten tauko tai jatka, on todiste riittämättömästi kehitetystä algoritmista ongelman ratkaisemiseksi.

Käytännössä ohjelmakoodi on kuitenkin usein tietue jo olemassa olevasta, aiemmin laaditusta algoritmista, jota ei ole tarkoituksenmukaista muokata puhtaasti teknisistä syistä. Yritys korvata varhainen poistumiskomento tällaisessa koodissa rakenteellisilla rakenteilla osoittautuu usein tehottomaksi tai hankalaksi. Esimerkiksi yllä oleva koodinpätkä komennolla breakvoitaisiin kirjoittaa näin:

// Varhainen poistuminen silmukasta ilman taukoa bool flag = false ; // lippu ennenaikaisesta lopettamisesta while ( < ehto > && ! lippu ) { ... operaattorit if ( < virhe > ) { lippu = tosi ; } muu { ... operaattorit } } ... ohjelman jatkoa

On helppo varmistaa, että fragmentti toimii samalla tavalla kuin aiemmat, erona on vain se, että silmukasta suoraan poistumisen sijaan aikaisen poistumisen lippu asetetaan virheentarkistuspaikkaan, joka tarkistetaan myöhemmin normaali ehto silmukan jatkamiselle. Aikaisen poistumiskomennon kumoamiseksi ohjelmaan oli kuitenkin lisättävä lipun kuvaus ja ehdollisen operaattorin toinen haara, ja ohjelman logiikka "hämärtyi" (päätös ennenaikaisesta lopettamisesta tehdään yhdessä paikassa, ja teloitettiin toisessa). Tämän seurauksena ohjelmasta ei ole tullut yksinkertaisempaa, lyhyempää tai selkeämpää.

Tilanne on hieman erilainen ohittaa iteraatio -komennon kanssa. Se korvataan yleensä erittäin helposti ja luonnollisesti ehdollisella operaattorilla. Esimerkiksi yllä oleva taulukon summanpätkä voitaisiin kirjoittaa näin:

int arr [ ARRSIZE ]; ... // Kaikkien ja vain positiivisten taulukon arr-elementtien yhteenlaskeminen erikseen // korvaamalla jatkuu int sum_all = 0 ; int summa_pos = 0 ; for ( int i = 0 ; i < ARRSIZE ; ++ i ) { summa_all += arr [ i ]; if ( arr [ i ] > 0 ) // Ehto päinvastainen! { summa_pos += arr [ i ]; } }

Kuten näette, riitti korvata tarkistettu ehto vastakkaisella ja laittaa silmukan rungon viimeinen osa ehdolliseen lauseeseen. Näet, että ohjelma on lyhentynyt (johtuen skip iteration -komennon poistamisesta) ja samalla loogisempi (koodista näkyy suoraan, että positiiviset elementit summataan).

Lisäksi iteroinnin ohituskomennon käyttö silmukassa, jossa on ehto (while-loop), voi myös aiheuttaa ilmeisen virheen: jos silmukan runko, kuten usein tapahtuu, päättyy komentoihin silmukkamuuttujien muuttamiseksi, iteraatio skip-komento ohittaa myös nämä komennot, minkä seurauksena (riippuen siitä, missä olosuhteissa ohitus tapahtuu) voi tapahtua silmukka tai iteraatiotoisto, joka ei vastaa algoritmia. Joten jos korvaamme for-silmukan whileilla yllä olevassa esimerkissä, saamme seuraavan:

int arr [ ARRSIZE ]; ... int summa_all = 0 ; int summa_pos = 0 ; int i = 0 ; while ( i < ARRSIZE ) // Silmukka näyttää samalta kuin edellinen ... { summa_all += arr [ i ]; if ( arr [ i ] <= 0 ) jatka ; summa_pos += arr [ i ]; ++ i ; // ... mutta tämä komento ohitetaan jatkettaessa // ja ohjelma kiertää }

Huolimatta rajallisesta hyödyllisyydestään ja mahdollisuudesta korvata ne muilla kielirakenteilla, iterointikomennot ja erityisesti silmukan varhainen poistuminen ovat joissain tapauksissa erittäin hyödyllisiä, minkä vuoksi ne säilytetään nykyaikaisissa ohjelmointikielissä.

Sisäkkäiset silmukat

On mahdollista järjestää silmukka toisen silmukan rungon sisään. Tällaista silmukkaa kutsutaan sisäkkäiseksi silmukaksi . Sisäkkäistä silmukkaa suhteessa silmukkaan, jonka runkoon se on sisäkkäinen, kutsutaan sisäsilmukaksi ja päinvastoin, silmukkaa, jonka rungossa on sisäkkäinen silmukka, kutsutaan ulkoiseksi suhteessa sisäkkäiseen silmukkaan. Sisäkkäisen silmukan sisällä puolestaan ​​voidaan upottaa toinen silmukka, joka muodostaa seuraavan sisäkkäisen tason ja niin edelleen. Pesätasojen määrää ei yleensä ole rajoitettu.

Sisäsilmukan rungon suoritusten kokonaismäärä ei ylitä sisäisen ja kaikkien ulompien silmukoiden iteraatioiden lukumäärän tuloa. Esimerkiksi ottamalla kolme sisäkkäistä silmukkaa, joissa kussakin on 10 iteraatiota, saamme 10 rungon suoritusta ulommalle silmukalle, 100 toisen tason silmukalle ja 1000 sisimmälle silmukalle.

Yksi sisäkkäisiin silmukoihin liittyvistä ongelmista on niistä varhaisen poistumisen järjestäminen. Monilla ohjelmointikielillä on silmukan pääteoperaattori ( Cbreak : exitssä, Turbo Pascalissa, lastPerlissä jne.), mutta yleensä se tarjoaa poistumisen vain sen tason silmukasta, josta se kutsuttiin. Sen kutsuminen sisäkkäisen silmukan sisältä päättää vain tämän sisäisen silmukan, kun taas ulompi silmukka jatkaa toimintaansa. Ongelma saattaa tuntua kaukaa haetulta, mutta joskus se ilmenee ohjelmoitaessa monimutkaista tietojenkäsittelyä, kun algoritmi vaatii välittömän keskeytyksen tietyissä olosuhteissa, joiden olemassaolo voidaan tarkistaa vain syvälle sisäkkäisestä silmukasta.

Sisäkkäisistä silmukoista poistumisen ongelmaan on useita ratkaisuja.

  • Yksinkertaisinta on käyttää goto -operaattoria hyppäämään ohjelman pisteeseen välittömästi sisäkkäisen silmukan jälkeen. Strukturoidut ohjelmoijat arvostelevat tätä varianttia , kuten myös kaikkia goton käyttöä vaativia konstruktioita . Joissakin ohjelmointikielissä, kuten Modula-2 , ei yksinkertaisesti ole ehdotonta haaraoperaattoria, eikä tällainen konstruktio ole niissä mahdollista.
  • Vaihtoehtona on käyttää tavanomaisia ​​silmukan lopetustyökaluja tarvittaessa asettamalla erityisiä lippuja, jotka edellyttävät käsittelyn välitöntä loppuun saattamista. Haittapuolena on koodin monimutkaisuus, suorituskyvyn heikkeneminen.
  • Sisäkkäisen silmukan sijoittaminen proseduuriin. Ajatuksena on, että kaikki toiminnot, jotka on ehkä keskeytettävä etukäteen, esitetään erillisenä toimenpiteenä ja ennenaikaiseen lopettamiseen käytetään proseduurin poistumiskäskyä (jos sellainen on ohjelmointikielessä). Esimerkiksi C-kielessä voit rakentaa funktion sisäkkäisellä silmukalla ja järjestää sen poistumisen return-käskyn avulla . Haittapuolena on, että koodinpätkän valinta prosessiin ei aina ole loogisesti perusteltua, eikä kaikilla kielillä ole säännöllisiä keinoja toimenpiteiden varhaiseen loppuun saattamiseen.
  • Hyödynnä poikkeusten (poikkeustilanteiden) luomis- ja käsittelymekanismi, joka on nyt saatavilla useimmilla korkean tason kielillä . Tässä tapauksessa epänormaalissa tilanteessa sisäkkäisen silmukan koodi herättää poikkeuksen, ja poikkeuksenkäsittelylohko, johon koko sisäkkäinen silmukka sijoitetaan, ottaa sen kiinni ja käsittelee sen. Haittapuolena on, että poikkeusten käsittelymekanismin toteutus on useimmissa tapauksissa sellainen, että ohjelman nopeus laskee. Totta, nykyaikaisissa olosuhteissa tämä ei ole erityisen tärkeää: käytännössä suorituskyvyn menetys on niin pieni, että sillä on merkitystä vain harvoille sovelluksille.
  • Lopuksi on olemassa erityisiä kielipalveluita sisäkkäisistä silmukoista poistumiseen. Joten Ada-kielessä ohjelmoija voi merkitä silmukan (sisäkkäisen silmukan ylimmän tason) etiketillä ja osoittaa tämän nimiön silmukan varhaisen lopettamisen komennossa. Poistuminen ei tapahdu nykyisestä silmukasta, vaan kaikista sisäkkäisistä silmukaista merkittyyn silmukaan asti, mukaan lukien [3] . PHP-kieli tarjoaa mahdollisuuden määrittää keskeytettyjen jaksojen lukumäärän komennon jälkeen break - tämä break 2keskeyttää itse ja sen yläpuolella olevan jakson, ja break 1se vastaa yksinkertaisesti komennon kirjoittamista break[4] .

Silmukat, joissa on useita suojattuja haaroja

Dijkstran sykli

Ohjelmointiteoriassa on toinen syklisen rakentamisen muoto, joka poikkeaa pohjimmiltaan "klassisista" ja jota kutsutaan Dijkstra-sykliksi Edsger Dijkstran mukaan, joka kuvasi sen ensimmäisenä. Klassisessa Dijkstra-kuvauksessa tällainen sykli näyttää tältä:

tehdä P 1 → S 1 , … P n → S n od

Tässä do on silmukkarakenteen alun merkki, on silmukan konstruktion od lopun merkki, P i  on i -s suojaehto (looginen lauseke, joka voi olla tosi tai epätosi), S i  on i - vartioitu komento . Silmukka koostuu yhdestä tai useammasta haarasta ( vartioitu ilmaisu ), joista jokainen on vartiointiehdon (tai lyhyesti "vartijat") ja vartioidun komennon pari (on selvää, että todellisuudessa komento voi olla monimutkainen).

Kun Dijkstran silmukka suoritetaan, suojaehdot lasketaan jokaisessa iteraatiossa. Jos ainakin yksi niistä on tosi, suoritetaan vastaava suojattu komento, jonka jälkeen alkaa uusi iteraatio (jos useampi kuin yksi vartiointiehto on tosi, suoritetaan vain yksi suojattu komento). Jos kaikki suojaehdot ovat vääriä, silmukka päättyy. On helppo nähdä, että Dijkstran silmukka, jossa on yksi vartiointiehto ja yksi vartijakomento, on itse asiassa tavallinen silmukka, jossa on ennakkoehto ("while" -silmukka).

Vaikka Dijkstra-silmukka keksittiin jo 1970-luvulla, sen luomiseen ei ole erityisiä rakenteita ohjelmointikielissä. Ainoa poikkeus oli äskettäin luotu Oberon-07  , ensimmäinen oikea ohjelmointikieli, joka tukee nimenomaisesti silmukkaa, jossa on useita suojattuja haaroja. Dijkstran sykli voidaan kuitenkin mallintaa ilman suuria vaikeuksia käyttämällä perinteisiä strukturoitujen ohjelmointikielten rakenteita. Tässä on esimerkki sen toteutuksesta yhdellä mahdollisista tavoista Ada-kielellä:

silmukka jos P1 niin S1 ; ... elsif Pn sitten Sn ; muu poistu ; loppu jos ; end - silmukka ;

Tässä P1-Pn ovat suojaolosuhteet ja S1-Sn vastaavat suojakomennot.

Dijkstran silmukka on hyödyllinen joidenkin tiettyjen toistuvien laskutoimitusten toteuttamisessa, joita on hankala kuvata perinteisemmillä silmukkarakenteilla. Esimerkiksi tämä sykli edustaa luonnostaan ​​äärellistä automaattia  — jokainen haara vastaa yhtä automaatin tilaa, vartioidut olosuhteet rakennetaan siten, että nykyisessä iteraatiossa valitaan automaatin nykytilaa vastaava haara ja vartioidun koodi. käsky varmistaa, että laskelmat suoritetaan nykyisessä tilassa ja siirtyvät seuraavaan (eli sellainen muuttujien muutos, jonka jälkeen halutun haaran suojaehto on tosi seuraavassa iteraatiossa).

Spider Cycle

On helppo nähdä, että Dijkstran silmukka ei sisällä eksplisiittistä jatkamis- tai poistumisehtoa, jota kaikki ohjelmointiteoreetikot eivät pidä siunauksena. Siksi ehdotettiin monimutkaista Dijkstra-syklin rakennetta, jota kutsutaan "hämähäkkisykliksi". Samassa merkinnässä se näyttää tältä:

tehdä P 1 → S 1 , … P n → S n ulos Q 1 → T 1 , … Q n → T n muu E od

Tässä merkin jälkeenout lisätään valmistumishaarat , jotka koostuvat poistumisehdoista Qi ja lopetuskäskyistä Ti . Lisäksi on lisätty vaihtoehtoinen täydennyshaara E-komennolla .else

Hämähäkkisilmukka suoritetaan seuraavasti:

  • Vartiointiolosuhteet on laskettu. Jos todellinen vartiointiehto on olemassa, vastaava suojakomento suoritetaan.
  • Poistumisehdot lasketaan. Jos todellinen poistumisehto on olemassa, suoritetaan vastaava lopetuskomento, jonka jälkeen silmukan suoritus päättyy. Jos kaikki poistumisehdot ovat epätosi, seuraava iteraatio alkaa, mutta vain jos ainakin yksi suojaehdoista oli tosi nykyisessä iteraatiossa.
  • Jos tässä iteraatiossa kaikki suojaehdot ja kaikki poistumisehdot ovat vääriä, suoritetaan alt-end-käsky E, jonka jälkeen silmukan suoritus keskeytyy.

"Spider"-syklin rakenne mahdollistaa erittäin tiukan kuvauksen syklin suoritusolosuhteista. Teoreettisten näkemysten mukaan vaihtoehtoisen täydennyksen haaraa ei pidä käyttää yhtenä vaihtoehdoista silmukan oikeaan päättämiseen (kaikki sellaiset vaihtoehdot tulee muotoilla vastaaviksi täydennyshaaroiksi, joissa on eksplisiittinen ehto), se palvelee vain tilanteen seuraamista, kun jostain syystä, jostain syystä kierto alkoi sujua epänormaalisti. Toisin sanoen alt-komento voi vain analysoida virheen syitä ja esittää analyysin tulokset.

Vaikka selkeää syntaksitason tukea tälle silmukalle ei ole olemassa missään ohjelmointikielessä, hämähäkkisilmukka, kuten Dijkstran silmukka, voidaan mallintaa käyttämällä perinteisiä rakennerakenteita.

Silmukan optimointimenetelmät

lähdekoodin vastaavat muunnokset kääntäjä

Katso myös

Muistiinpanot

  1. Oberon on Niklaus Wirthin unelma
  2. Tarkkaan ottaen identiteetti ei ole täydellinen, koska jatka-lause toimii eri tavalla.
  3. Silmukat/Sisäkkäiset Rosetta  Codessa
  4. ↑ PHP Manuaali , tauko 

Linkit