Perlo „range“ operatorius

EnC manęs PHP forume paprašė paaiškint, kaip veikia Perlo „range“ operatorius. Nesinori PHP forumą teršti perliniais
dalykais, tad bandysiu tai paaiškinti čia. Beje, visa tai ką čia rašau galima rasti Perlo dokumentacijoje (konsolėje tereikia surinkt perldoc perlop).

Perlo „range“ operatorius žymimas dviem taškais, ir reiškia faktiškai du dalykus. Pirmoji reikšmė yra labai paprasta –
parašius 1..10 gausime reikšmių nuo 1 iki 10 sąrašą (list). Būtent ši operatoriaus reikšmė naudojama masyvų kontekste
(list context – kai reikia jog išraiška grąžintų masyvą, o ne skaliarą). Šitaip labai lengvai galima formuoti masyvus:

my @masyvas = 1..10;
# tas pats kaip @masyvas = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Tačiau pats populiariausias šio dalyko panaudojimo būdas yra cikluose, nes vietoj sunkiai įskaitomo

for($i = 1; $i <= 10; $i++) {
# ... kažką darom 10 kartų
}

galima parašyti trumpai ir aiškiai:

for(1..10) {
# ... kažką darom 10 kartų
}

Kitas dažnas šio operatoriaus panaudojimas tame pačiame kontekste yra masyvų dalijimas (slice):

my @foo = (1, 2, 3, 4, 5, 6);
my @bar = @foo[3..5];
# @bar dabar yra (4, 5, 6);
my @baz = @foo[3..$#foo];
# @bar dabar irgi yra (4, 5, 6);

„Range“ operatoriui galima perduot ne vien skaitmenis, bet ir simbolių eilutes (pagal tam tikrą algoritmą bus didinami simbolių eilutės ASCII kodai), tad šis Perlo sakinys atspausdins visą anglišką abėcėlę:

print for 'a'..'z';

Arba tiesiog:

print 'a'..'z';

Vienas geriausių šio operatoriaus pliusų yra tas, kad jis neužsigrobia daug atminties yra intervalo „neišplečia“ atmintyje jei to būtinai nereikia, tad for(1..1000000) {...} nesukuria laikinojo masyvo su milijonu įrašų.

Jei intervalų sudarymas iš simbolių eilučių atrodo ganėtinai magiškai, tai patys didžiausi stebuklai prasideda tada, kai
„range“ operatorius naudojamas skaliariniame kontekste (kai išraiška turi grąžinti skaliarą, o ne masyvą). Dažniausiai taip šis operatorius naudojamas if sąlygoje. Šis operatorius grąžina 0 (false) iki tol kol kairė šio operatoriaus pusė pradeda
grąžint true, tada grąžina true iki tol kol dešinė pusė pradeda grąžint true. Lengviausia turbūt suprast pavyzdžiu:

while(<>) {
print if /PRADZIA/../PABAIGA/;
}

Šis kodas skaito iš bylos eilutes, ir spausdina tik tas, kurios yra tarp eilučių, atitinkančių įpraiškas /PRADZIA/ ir
/PABAIGA/. Tarkime jei byla bus:

foo
bar
PRADZIA
kazkas
dar
PABAIGA
nu
gi

bus atspausdinama tik kazkas ir dar (nespausdina kol neatitinka įpraiškos /PRADZIA/, o tada spausdina iki tol kol atitinka įpraišką /PABAIGA/) Magija čia nesibaigia. Jei vietoj įpraiškos įrašysim skaičių, jis bus magiškai lyginamas su $., kuris reiškia eilutės numerį. Tad kodas

print if 101..200;

atspausdins antrąjį šimtą eilučių.

6 Comments

  1. <quote>&ETH;is operatorius gr&agrave;&thorn;ina 0 (false) iki tol kol kair&euml; &eth;io operatoriaus pus&euml; pradeda gr&agrave;&thorn;int true, tada gr&agrave;&thorn;ina true iki tol kol de&eth;in&euml; pus&euml; pradeda gr&agrave;&thorn;int true.</quote>

    vat ash ir nesuprantu prie ko chia tas /(TRUE|FALSE)/ grazhinimas /(kaireje|deshineje)/ ishraishkos puseje :S

  2. Na, pakeiskim taip:

    while (<>){

    print if some_cond;

    }

    print (kuris šiuo atveju pagal nutylėjimą spausdins kintamojo $_ reikšme, t.y. eilutę iš failo) bus atliekamas tik tada, kai some_cond yra true.

    Kai tas some_cond yra /PRADZIA/../PABAIGA/ galima

    į jį žiūrėti kaip į trigerį

    Kas vyksta veikian skriptui:

    while (<>) {…} reiškia, kad skaitoma iš STDIN'o, kol ten kažkas bus ir nuskaityta eilutė automatiškai priskiriama kintamajam $_. Pakišus failą, bus skaitomos eilutės iš failo.

    Toliau eina print, kuris išspausdina tą, kintamąji, bet tik su sąlyga, kad some_cond yra true.

    Vietojs some_cond galime naudoti įpraišką, pvz. /^#/. Perle tokia "plika" įpraiška veikia taip, kad ji pritaikoma tam pačiam magiškajam $_ ir gražina true arba false, priklausomai, ar atitiko šablonas ar ne.

    Taigi, jei ta eilutė būtų

    print if /^#/;

    Tai būtų spausdinamos tik tos failo eilutės, kurios prasideda #.

    Dabar priskasėm iki reikalo esmės:

    kai some_cond yra konstrukcija /įpraiška1/../įpraiška2/, tai

    ji veikia kaip trigeris: iš pradžių gražina false, kol nuskaityta eilutė atitinka įpraiška1. Nuo tada imama gražinti true, ir taip tesiasi iki tol, kol nuskaityta eilutė atitiks įpraiška2, tada vėl gražinama false.

    Mūsų atveju – prabėgamas faila ir išvedamos tik eilutės esančios tarp eilučių, kuriose yra PRADZIA ir PABAIGA (beje, bus atspausdinta ir tos abi eilutės).

    Tikiuosi moxliukas nesupyks už mano trigrašį ir pataisys, jei nusišnekėjau.

  3. ;) Būtent taip ir yra. Nevartojau žodžio "trigeris", nes gal jis ne visiem įprastas ;)

Comments are closed.