juuli 09, 2015

Regulaaravaldised ja grep II

Laiendatud GREP ehk GREP -E


Kui iganes vajate regulaaravaldisi, kasutage kohe
grep -E ... varianti.
Teisiti öeldes kasutage laiendatud regulaaravaldisi,mis ei olegi edasijõudnule nii väga laiendatud.
Siis võib proovida grep -P (perli standard). Kui juba niikaugel ollakse, oleks päris mõistlik pärast seda selgeks õppida ka programmeerimiskeel Perl ja kasutada selle asemel Perli sisseehitatud regulaaravaldiste süsteemi.
Aga pidagem meeles Zawinski mõrudat aforismi - ühe regulaaravaldiste süsteemi puhul on teil vaid 2 probleemi, kui aga kasutate mitut regulaaravaldiste süsteemi ühteaegu, kasvab probleemide arv veelgi. Parem siis kohe enam-vähem korralik regex.

MIS EI OLE REGULAARAVALDIS

Unixi failide otsimise mustrid ei ole regulaaravaldised, üleüldse kõik, mis käib termini globbing alla. globbing tulenes Unixi omaaegsest kombest enne „õigele” käsule andmete üleandmist failide loetelu laiendada. Seda tegi käsk /etc/glob.
Ka praegu teeb seda funktsioon glob() ja teeb mõne kesta skripti käitumise päris ettearvamatuks.
Et globbingu eest pääseda, tuleb unixi kestas kirjutada kõik tekstid ühekordsete ülakomade sisse. Kahekordsete puhul saab sõnedest argumentide sisse smugeldada „keskkonnamuutujad”.

PERLI REGULAARAVALDISED

ei ole ka päris regulaaravaldised, sest Kleene algebra neid võimalusi ei kasuta, mis Perli regulaaravaldistes sees on. Nii on päris hea alustada või lähtuda „laiendatud regulaaravaldistest”, mis tegelikult vastab päris hästi Kleene poolt kirjeldatud regulaarsetele hulkadele ja teeb ikkagi natuke rohkem.

Unustame nüüd teoreetilise tarkuse ja asume asja juurde.

Katsun asju esitada probleem - lahendus stiilis.

I Kuidas teostada literaalset otsingut kirjavahemärkidega teksti puhul.

Ülesanne: Kirjutada grep -E käsurida, nii et literaalselt, s.t. täht-tähelt otsiks see teksti seest stringi
!"#%&_,/:;=@+*().?-[\]$^{|}'<>


Oma töö kontrolliks looge fail kirjavahemargid ja pasteerige sinna tekst

Arno kirjutas krihvliga tahvlile:

Kirjavahemärgid arvutiasjanduses on sellised !"#%&_,/:;=@+*().?-[\]$^{|}'<>

ja neid on veel ja veel, aga need on kõige kasutatavad



Käsureal saab katsetada oma otsinguid järgmiselt, olgu hakatuseks võetud regulaaravaldis '!”#'
grep -E -e '!”#' kirjavahemargid
Kui saate kirjavahemärkidega rea failist kätte, võib proovida käsuritta uusi kirjavahemärke lisada ja jälle testida.
See meetod on hea ka edaspidi.
Ära usalda aga kontrolli nii manuaale aga ka oma arusaamist regulaaravaldistest.
Õnneks läheb literaalse vastavuse otsing ludinal kuni + märgini. Siis aga algavad erimärgid, need on laiendatud regulaaravaldiste puhul sellised:
+*().?-[\]$^{|}
Ülakoma märk ei ole erimärk, aga lõpetab egrepi / grep -E stringi/sõne ja ja ka seda peab eraldi kohtlema, alustades kohe uut sõnet (vt. allpool). '><' märgid ka ei ole erisümbolid, aga langujoon annab neile eritähenduse, seetõttu ma panin nad ka siia ritta.



Erimärkide erikohtlemine regulaaravaldistes

Regulaaravaldistes kohtab kahte tüüpi metasümboleid:
* ühesümbolilised erimärgid +*().?-[\]$^{|}
* Escape ehk paosümboli '\' järel tulevate jadade abil moodustatud metasümbolid / käsud.
Paosümbolit '\' kasutatakse ka metasümbolitelt eritähenduse eemaldamiseks.
'\*' tähistab regulaaravaldist, mis otsib teksti seest * märgi esinemist.
Nii on ka kõikide teiste ülaltoodud metasümbolitega, kaasa arvatud langujoon ise:
'\\' esitab literaalselt langujoont regulaaravaldises.
Kui aga sümbolil endal ei ole metatähendust, võib ta selle omandada, kui selle ette kirjutada langujoon.
Egrepis on selliselt vermunud metasümboleiks
\b ,\B, \w,\W ja \< ja \>.
\b on sõnapiire, \B mittesõnapiire, \w tähistab sümbolit, mis võib kuuluda sõnasse (nii nagu regex sellest parajasti aru saab) , \W on sümbol, mis ei kuulu sõnasse.
\< on sõna alguse piire, \> tähistab sõna lõpupiiret.



Niisiis on kuni ülakomani metasümbolite literaalse esituse probleem lahendatud:
grep -E -e '!"#%&_,/:;=@\+\*\(\)\.\?\-\[\\\]\$\^\{\|\}' kirjavahemargid
Ülakoma literaalse sisestamise jaoks tuleb sõne lõpetada ' -ga, kesta bash pääsusümboliks oleva '\' abil sisse smugeldada üks ülakoma
ning lisada märgid '<' ja '>' juba uuete ülakomade vahel.
grep -E -e '!"#%&_,/:;=@\+\*\(\)\.\?\-\[\\\]\$\^\{\|\}'\''<>' kirjavahemargid
Sama jama saab ära teha plokk-escape sümbolitega \Q ja \E Perli grepis:
grep -P -e '\Q!"#%&_,/:;=@+*().?-[\]$^{|}\E' kirjavahemargid



II Kuidas kirja panna korralikus regulaaravaldise keeles Windows / bash stiilis otsinguid?
Oletame, et otsime faili nimega „Kui Arno isaga koolimajja jõudis”.
Kuidas seda kiirelt teha? Unix bashis leiab selle kiiresti
ls Kui*isaga* abiga.
cp kevade 'Kui Arno isaga koolimajja jõudis'
ls Kui*isaga*
Tekstifailist kevade aga sellise regulaaravaldise mustri abil te ei leia mitte midagi. Proovige!
grep -E -e 'Kui*isaga*' kevade
Või otsing 'Arn?'
cp kevade Arno
ls Arn?
töötab, aga
grep -E -e 'Arn?' tegelikult petab meid, sest annab välja read, kus sisaldub Ar või Arn, see pole üldsegi sama otsing.
Vastus algab erisümbolist punkt '.'
Küsimärgi asemel on regulaaravaldises PUNKT. Nii lihtne see ongi!
Punkt klapib iga sümboliga. Otsing
grep -E -e 'Arn.' kevade on samaväärne tehe kevade tekstifaili suhtes, mis bashis Arno nimelist faili taga ajada - ls Arn?
'*' -l on eristaatus regulaaravaldiste maailmas. See tähistab tehet „null või enam korda”,
rakendatuna tärnile eelnevale sümbolile, VÕI eelnevale alam-regulaaravaldisele, mis peab sel juhul olema sulgudesse eraldatud.
Näiteks ab(ab)* tähistab nüüd seda, et ab asemel klapib otsingutega ka sümboljada
abab, abababa... ja nii edasi ....
Kui seda rakendada PUNKTILE, saame igasuguse sümbolite jada.
'.*' tähistab suvalist sümbolite jada.
Teisiti öelduna peaks bashi otsingu ekvivalendina * asemele kirjutama '.*'
Järelikult failiotsingu ls Kui*isaga* ekvivalendiks failist kevade oleks
grep -E -e 'Kui.*isaga.*' kevade.
Tehtemärki * nimetakse Stephen Kleene auks veel „Kleene'i tärniks/täheks” (Kleene star).
Kas 0 tõi täpselt 1 kord:
'?' tähistab aga Kleene maailmas valikut - eelneva sümboli / alamavaldise esinemist 0 või 1 kord.
Nii klapib avaldis 'Arn?o' nii sõnedega 'Aro' kui ka 'Arno' ...
III Kas Arno või Teele?
Täiendame Kevadet omade lausetega. Kirjutame sinna ka rea „Kui Teele isaga koolimajja jõudis, ei olnud koolipäev veel alanudki ...”.
olgu see shedööver kirjas failis kevade3.
sed '1 a Kui Teele isaga koolimajja jõudis, ei olnud koolipäev veel alanudki' kevade >kevade3
sed - stream editor selle käsuga lisab reale 1 täiendava rea ja väljund jõuab faili kevade3.
Nüüd
grep -E -e 'Kui (Arno|Teele)' kevade3
trükib välja mõlemad read, sest '|' on alterneerimise tehte sümbol, võimaldades OR valikut
erinevate alammustrite vahel, praegu siis Arno või Teele.
Sulgude puudumisel oleksid alammustrid 'Kui Arno' või 'Teele'.
Võite kontrollide - kustutage (juba editoriga) 'Kui' Teelega seotud realt ära ja proovige mustreid sulgudega ja ilma.
IV Süntaktilise suhkrutõve algus: avaldised parooli kõvaduse testimiseks.
Eespooltoodud konstruktsioonidega saavad matemaatikud hakkama - kõik Kleene mõttes regulaarsed avaldised on nii konstrueeritavad ja kasutatavad. Aga mõned avaldised lähevad kole pikaks.
Olgu meil näiteks vaja välja otsida read, mis sisaldavad vähemalt ÜHE avaldise abc, aga võib-olla
rohkem. Selleks tuleb abc välja kirjutada 2 korda:
'abc(abc)*'
Või olgu meil vaja kontrollida, sisaldab see rida abc-d 4-8 korda.
'abcabcabcabc(abc)?(abc)?(abc)?(abc)?'
Esimese variandi jaoks on leiutatud operaator '+', mis temale eelneva sümboli / alammustri puhul seab nõudeks ekisteerida vähemalt 1 kord.
nii et '(abc)+' 'abc(abc)*' asemel.
Teise variandi puhul kasutatakse kvantoreid {m,n}
See avaldis tähistab temale eelneva sümboli / alamavaldise esinemist vähemalt m korda ja maksimaalselt n korda.
{m,} tähistab avaldise esinemist m ja enam korda
{m} täpselt m korda.
Pika
abcabcabcabc(abc)?(abc)?(abc)?(abc)?'
asendab '(abc){4,8}'
Kõiki sarnaseid mugandusi, mis võimalusi juurde ei loo, nimetatakse süntaktiliseks suhkruks ja esialgu on sellega kõik OK. Kuid kõik mist algab hästi, lõpeb halvasti, ütleb Murphy ja nii on siingi läinud.
Te näete peagi, et suhkrust saab peatselt sool, kuna regulaaravalduste süntaks PERL standardis on võrdlemisi keeruline ja võimalusi muudkui lisandub...
Kindlasti vajalikud aga on TÄHEKLASSID.
Olgu meil näiteks alterneerimise sümboli abiga vaja kirja panna avaldis 3 kohalise täisarvu tuvastamiseks.
Saaksime midagi sellist:
'(1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9){2}'
Ühe sümboli valikuks on loodud nurksulgudes sisestavad täheklassid.
kolmekohaline täisarv oleks siis selline:
'[1-9][0-9]{2}'
Nurksulgude sees on erisümboli tähendus langujoonel '\', katusel ^, sidekriipsul -, ülejäänud sümbolid kaotavad oma väe nurksulgude sees.
Sidekriips mitte esimeses positsioonis tähistab vahemikku. Esimeses positsioonis kohe esimese nurksulu järel on ta tavaline sidekriips.
Näiteid vahemikest:
[1-9] - numbrimärgid vahemikust 1...9.
[a-z] - inglise tähestiku väiketähed kindlalt ning korrektsete regionaalsete /jms. valikute korral ka täpitähed....



A-Z tähistavad siis suurtähti.
Ikkagi tuleks langujoon, sidekriips ja katus märgistada langujoonega, parem lugeda, isegi siis, kui sidekriips on esimeses positsioonis, kus ta vahemikku ei saa kuidagi tähistada.
Katus tähistab eitust esimeses positsioonis.Nii et [^0-9] võiks tähistada mitte numbrimärki.
Isegi „laiendatud regulaaravaldiste” kitsas võimaluste ruumis on [] vahel kasutusel kindlad täheklassid, kasutage neid alati näiteks [0-9] asemel.
{Perl - ühilduvas regulaaravaldistes on nende asemel alati võimalik kasutada \d stiilis sümboleid,
\d tähistab siin numbrit (digit).
}
Need on
[:alnum:] - tähed ja numbrid, \w on sama, mis [:alnum:], \W sama, mis [^.[:alnum:]], btw, täheklasse saab ka eitada katuse sümboli abil...
[:alpha:] - tähed
[:cntrl:] - kontrollsümbolid
[:digit:] - numbrid
[:graph:] - graafilised sümbolid
[:lower:] - väiketähed
[:print:] - prinditavad sümbolid
[:punct:] - kirjavahemärgid
[:space:] - „tühi ruum
[:upper:] - Suured tähed
[:xdigit:] - heksadetsimaalne arv.
Paneme lõpuks kirja ka käsu, mis kontrolliks põhimõtteliselt, kas paroolifailis sisalduvad paroolid
sisaldavad vähemalt ühte suurtähte, väiketähte, numbrit ning on vähemalt 6 sümbolit pikk:
pikkus: '.{6,}'
üks suurtäht: '[[:upper:]]'
üks väiketäht: '[[:lower:]]'
üks number: '[[:digit:]]'



grep -E -e '.{6,}' parool | grep -E -e '[[:upper:]]' | grep -E -e '[[:lower:]]' | grep -E -e '[[:digit:]]'
Et neid nõudeid kontrollida, tuleks regulaaravadliste klapikontrolli mitu korda sõltumatult rakendada. Praegu saab selleks kasutada Unixi „torusid”.
Sarnasel viisil võib kontrollida ka seda, kas kevade faili mingi rida sisaldab korraga sõnu „Arno” ja „isaga” etc.. sõltumata sõnade järjekorrast. See oleks üks võimalus teha AND operatsiooni sõnade tuvastamisel ridades.
Pisut mugavamalt saab mitme tingimuse üheaegset kontrolli teha Perli greppi kasutades ning edasivaate või tagasivaate ankruid rakendades. Sellest edaspidi.
V Ankrud:
Ülesanne: Lugeda üle mingi projekti seest kõik koodiread, jättes välja tühjad read ja kommentaarid.
Sageli on vaja otsida tekstidest asju, mille paiknemine on teada - kas rea algus või rea lõpp.
Mõnikord on vaja otsida ka faili algusest või lõpust.
Oleme jõudnud nüüd kohta, kus projektijuht tahab lihtsalt ridade ülelugemise asemel (arendajad võivad olla kavalad ja lisada palju tühje ridu või ka tühje kommentaare stiilis // hei see on kommentaar) teada saada, palju ka tegelikult tööd tehakse.
Selleks on välja mõeldud ankrud / anchors.
Rea alguse ankruks on „katuse” sümbol
'^'.
Kõik read, mis algavad sõnaga 'Kui' saab kevade-st kätte käsuga
grep -E -e '^Kui' kevade
Rea lõpu ankruks on dollari märk.
Kõik read, mis lõpevad sõnaga 'alanud' ning lisaks ka kirjavahemärgid / tühikud, annab välja
grep -E -e 'alanud\W*$' kevade
Nüüd saab hakata lahendama koodiridade lugemise programmi probleemi. Teen seda C keele jaoks,
teiste keelte puhul on vajalik muuta ära koodi märkimiseks kasutatavad laiendid ja kommenteerimise reeglid.
Lisaksin, et ei ole mõtet ragistada ajusid vähemalt c keele puhul väga range lahendi jaoks.
Kui see keel tekkis, ei olnud see reapõhine, ühte ritta armastati kirjutada mitut käsku korraga ja oldi koguni uhked selle üle. Isegi sinna kuhugi vahele võis poetada mõne lühida, aga ülitäpse remargi. Ka rea lõppu võis ja võib kirjutada kommentaari.
Aga unixi enda filtrid olid kõik reapõhised.
See pxxxo on hakanud natukene hajuma, koodinikerdajad ise on avastanud, et võiks olla mingid vahendid, mis näiteks nende targad mõtted koodiridade vahelt jälle üles otsivad, tekitades automaatse dokumentatsiooni mulje.
Seetõttu tegin järgmised eeldused kommentaaride suhtes:
a) kommentaarid on reapõhised. Kas mingi rida on kommentaar või ei ole.
b) // -ga algav rida on kommentaar
c) /* märk rea alguses annab märku, et järgnevad read on kommentaarid, aga need on kuidagi eraldi ära märgitud. Näiteks c-s on traditsiooniline märk selleks *, seega * -ga algavad read olgu ka kommentaarid.
{Tegelikult ei ole, aga väga jabur on kirjutada koodi, kus rida alustada *-ga.}
d) */ märk rea ALGUSES lõpetab kommentaari. Ehk kusagil mujal ka, aga siis on tegemist üherealise kommentaariga stiilis ; /* hallo sallo */ ja seda koodirida ei tahaks arvestamata jätta.
e) kõik muu on kas kood või mingisugune kräpp, näiteks väljakommenteeritud kood.
Ka väljakommenteeritud kood on kood ja selle eest peab boss raha maksma.
Aluseks võtame juba väljamõeldud rea, mis õnnetuseks tühjad read ja kommentaarid ka tööks luges -
grep -Erch --include='*.c' --include='*.h' '' . | awk '{ sum += $1 } END { print sum }'

grep -le on vaja lisada
a) -v ja eemaldada '', loeme üle read stiilis kõik, mis ei ole kräpp
b) '^$' on tühja rea lugemiseks.
c) '^//' loeb reapõhiseid kommentaare
d) '^\*' neid ridu, mis on /* ja */ ridade vahel.
e) '^/\*' pika kommentaari algusrea märk
f) '^\*/' pika kommentaari lõpurida
Tulem:
grep -Erchv --include='*.c' --include='*.h' -e '^$' -e '^//' -e '^\*' -e '^/\*' -e '^\*/' . \
| awk '{ sum += $1 } END { print sum }'

Nonii. Vähemalt 10% arendajate palgast saab nüüd paremateks otstarveteks kasutada...



VI Piirded / Boundaries
Ülesanne: Lugeda ära „Kevade”-s / „Talve” -s sisalduvad sõnad ja nende sagedused ja püüda nende põhjal hinnata, kas Luts võis olla kirjutanud „Talve”.



Märkus:
See ülesanne on virtuaalne, ma ei kavatsegi seda lahendama hakata. Küll loeks hakatuseks meeleldi ära kõik sõnad minu juba sissetipitud lõigukesest „Kevade” alguses ja nende sageduse / arvu. Enamasti on see 1 või 2, seetõttu mingeidki järeldusi kirjutaja kohta vaevalt et annab teha.
Selle ülesande lahendamiseks tuleks tekstist välja eraldada sõnad. Ridade eraldajatega me juba tegelesime, rea alguse ja lõpu märkijaid võib ka eraldajateks lugeda.
Nüüd tegeleme sõnaeraldajatega.
Mõlemal juhul ei ole tegemist reaalsete sümbolitega, vaid teatud mõtteliste punktidega sümbolite vahel.

Katus ja dollar on rea alguse ja lõpu markerid, '\<' ja '\>' sõna alguse ja lõpu markerid.
Perlist on üle võetud sümbol
'\b', mis võib märkida nii sõna algust kui sõna lõppu.
Neid markereid teades on ülesanne üsna triviaalne.

Keda ei huvita ajalugu, minge loo lõppu, leiate sealt lahenduse.
Literaalne programmeerimine ja Unix
Kasutan siin juhust ja kirjeldan selle ülesandega seotuna ka kahe titaani, Donald Knuthi ja McIlroy vaidlust programmeerimise filosoofiate üle.
vt. http://www.leancrew.com/all-this/2011/12/more-shell-less-egg/
Enne vaidluse juurde asumist: ma olen süüdimatu oportunist, toetan kõike, mis töötab. Tähtis on, et programmid oleksid töötavad ja neist aru saadaks, ka seda ikkagi selleks, et nad ka edaspidi hästi töötaksid. Tähtis on, et kassid hiiri püüaksid.



Donald Knuth mõtles vaidluse ajal välja stiili, mida ta ise nimetas „kirjanduslikuks/literaalseks programmeerimiseks”. Teadvalt kasutas ta kahetähenduslikku sõna „literate” ning „kirjanduslik programmeerimine” on siiski jäänud huvitavaks, vahest ka arendavaks ideeks arvutiteaduses, mida ikka ja jälle üles korjatakse.
Minu arusaamise järgi oli ideeks hoida koos koodi ja koodi dokumentatsiooni, kirjutades mõlemad ühte dokumenti kokku. Koodi genereerimiseks tuli kasutada omaette makrode süsteemi, dokumentatsiooni genereerimiseks omaette makrode süsteemi,
kuigi vist peamiseks dokumendiks oli see literaalne programm ise, mis valmis nikerdatud? Jätan selle küsimuse lahtiseks. Äkki mõni arvutiteaduse instituudi doktorant lahendab selle ära?
Kood ise pidi pigem peegeldama kirjutaja mõtteid ja kavatsusi, mitte olema kompileerimise jaoks kokku pandud.
Edasi võite lugeda sellest ise Donald Knuthi teoseid uurides.
Probleem on selles, et sarnaseid mõtteid on väljendatud ikka ja jälle, erinevates kontekstides. Ja probleem on selles, et endiselt, 25 / 30 või 50 aastat hiljem ikkagi selgub, et kood on natukene erinev asi, kui koodi kirjeldav dokumentatsioon. Kui need kaks asja oleksid piisavalt lähedased, püsiksid programmid töötavatena koos kogu elutsükli vältel.
Äkki on veerandsada aastat „Literaalse programeerimise” algusest piisav aeg, et näha, et kokku nad ei saa küll kunagi, aga nad võivad muutuda lähedasteks, nagu Koit ja Hämarik.
Arvutiteaduse suvekoolide valgete ööde ajal võib see unistus ise
gi teatud määral täituda.
Niisiis kirjutas Knuth valmis 10 + leheküljelise programmi oma literaalse programmeerimise keskkonnas WEB. Mitte just palju, mitte aga väga vähe selle ülesande jaoks.
Seejärel McIlroy kiitis koodi väärtusi, aga seejärel lühidalt esitas OMA 6 realise lahenduse kirjeldatud probleemile, mõõta sõnade esinemissagedusi. Eesti keeles nii ehk teisiti tuleks kõvasti, rohkem vaeva näha, sest meie sõnad käänamisel tikuvad väga muutuma. Ikka need 14 käänet omastav,osastav, sisseütlev, seesütlev, seestütlev, alaltütlev, alalütlev, alaleütlev ja ninataga käänded otsa veel ...
Ma kopeerin ja pasteerin selle lahenduse kõigepealt siia,
surun ühele reale, jättes lõpuväljastuse (N popimat sõna välja trükkida, selle asemel trükin kõik, sest lõik oli niivõrd lühike), kirjutan ühe rea üle grepina, et õigustada selle esinemist siin.
Ja mõelgem edasi, kas probleemi lahendamiseks on sobivam 6 või 600 rida.
Originaalne McIlroy programm:

tr -cs A-Za-z '\n' | // asenda sõnavahed reavahetustega
tr A-Z a-z | // teisenda suurtähed väiketähtedeks
sort | // järjesta, et ühesugused sõnad oleksid järjest uniq -c jaoks
uniq -c | // kõrvalda samasugused read, kirjutada iga sõna juurde esinemissagedus
sort -rn | // sorteeri NUMBRILISE väärtuse järgi
sed ${1}q // trüki välja parameetrina antud arv ridasid, see rida jäetud minu poolt välja sõnade väikse arvu tõttu.


tulemus ühel real, võtta failist kevade:

tr -cs A-Za-z '\n' | tr A-Z a-z |sort |uniq -c |sort -rn



Toimiv rida, mis etteantud tekstifaili sõnad üle loeb ja ritta paneb:

tr A-Z a-z <kevade | grep -o -E -e '\b\w*\b' | sort | uniq -c | sort -rn

Märkus - sellest McIlroy programmireast esimene käsk ei toiminud hästi, täpitähed loobiti lihtsalt välja. Ümberkirjutus grepile aga toimis ja pikemate tekstide puhul toimiks ka suunamine sed-i.
Kogu ilu siin aga peitus regulaarses avaldises '\b\w*\b' ...


VII Tagasiviited ja regulaaravaldised

Ülesanne: Kirjutada kesta skriptina ühereakas (onliner) prime,

kasutades greppi ja regulaaravaldisi. Kui parameetrina anda ette arv, näiteks 17, siis

vastus on kas 1 (kui on tegemist algarvuga, 17 puhul ) või 0, kui ei ole algarvuga tegemist (20).
Vastus:
printf '1%0.s' $(seq 1 $1) | grep -E -vc -e '^(11+)\1+$'
Kopeerige see rida faili prime.
K
es teab, võib lisada ka sha bangi, kes ei tea, ei ole vaja.
chmod +x prime on aga vajalik. Skript käivitada Linuxis oma kataloogis olles, kasutage ./prime
(see on meenutus neile, kes on eluaeg windowsi stiiliga harjunud, k.a. allakirjutanu)



Enne kui asuda tagasiviidete vaatlemise juurde (mis EI OLE Kleene kavatsustes kunagi olnud), märgin, et printf ülesandeks on vaid prime -le parameetrina antud arvu pikkuse rea genereerimine, kasutades ühtesid.


'^(11+)\1+$'

klapib iga kordarvuga. grep pöörab vastuse 1-ks / või 0-ks, kui regulaaravaldises on klapp.
võtmete -vc abil.
\1 tähistab regulaaravaldistes tagasiviidet. Kui eraldi ei ole märgitud, siis laiendatud regulaaravaldiste mootorid jätavad meelde (või võime eeldada, et jätavad) sulgudes ära märgitud alamavaldised ning vastavalt sulgude järjekorrale tähistavad need \1, \2 ... \N -ga.
Praegu on meil vaid üks tagasiviide, mida tähistab \1.


Nimetatud tingimusele vastav sõne saab sisaldada vaid kordarv 1-sid ühes reas.
Kui sulgavaldiseks on 11, siis siit seeriast saame klappivateks avaldisteks 11, 1111, 111111..
ehk kõik paarisarvuliste 1-de arvuga read.
Kui sulgavaldiseks on 111, saame kõik 3 kordsete ühtede arvuga read.
Millega aga kuidagi klappi ei ole võimalik leida, on algarvulise arvuga 1-de read.
Kahjuks nimetatud skript ei ole väga arvutusvõimeline, minu linuxil hangus ta peaaegu juba viiekümnerealise ühtedejoru vaatlemisel.
Asja parandab aga oluliselt PERL-ühilduva regulaaravaldise kasutuselevõtt - asendage
(11+) saladusliku (11+?) -ga ja asendage grepi võti '-E' '-P' -ga (perli regulaaravaldistest arusaav grepp).


ÕIGE VASTUS:


printf '1%0.s' $(seq 1 $1) | grep -P -vc -e '^(11+?)\1+$'




IX:Sissejuhatus perli GREPPi:
Taluperemees Juhan pidas tekstifailis inventuuri oma loomade üle. Kirjed olid formaadis
NN lammast NNN lehma MMM siga ... ning edaspidi ei märkinud külamees enam oma faili koguarvu, vaid ainult juurdetulnud - mahaläinud loomi.
Kirjutada regulaaravaldised, mis sellisest failist tulemused aitaks kokku võtta.
näidisfail loomad
5 lammast
4 siga
6 jänest
5 lammast
1 lammas; -1 lammas (suri ära)
2 jänest; 3 jänest;
...
Lõpus veel järgmised kirjed:
lambaid 3
jäneseid -4
jäneseid 3
Ühel ilusal päeval ütles ta poeg Jukule, et ole hea ja liida arvutis need kokku. Juku käis õnneks ülikoolis ja ta sai kerge vaevaga hakkama kõikide koduloomade kokkuliitmisega, kasutades perli regulaaravaldisi ja awki ning pärast kõva pusimist oskas eraldi välja arvutada ka jäneste, lammaste ja sigade koguarvu.
Lahenduse algus tundub lihtne - vajalik oleks kasutada grep -Po -d ning suunata see meile juba hästi tuntud awk ühereakasse

awk '{ sum += $1 } END { print sum }'

Kõikide farmis elajate elajate koguarvu saab nii lihtsalt:
grep -Po -e "-?\d+" loomad | awk '{ sum += $1 } END { print sum }'

Märgime, et '-?\d*' tähistab negatiivset või positiivset täisarvu, \d on Perli grepis numbrimärgi tähis,
\D tähistab mittenumbrit.
Kuidas aga eraldada „sikud lammastest”, eriti kui mõnikord lammas on eespool numbrimärke ja mõnikord tagapool?
Ilmselt on vaja mingeid töövahendeid, et otsida arve, mille järel tuleb sõne „lammas”
või mille ees on sõne lambaid. Nende ja paljude teiste uute vahendite jaoks leiutas Larry Wall süntaksi '(?...) '
Sellest ning üleüldse küsimärgist on saanud regulaaravaldiste süntaktiline „sool”, üsna raskesti loetav ja desifreeritav sümbolijoru. Pealekauba teostavad sarnaselt väljanägevad käsud väga erinevaid asju.
Ümbervaatamise ankrud / eeldused (lookaround anchors) /assertions
Ettevaatavad ankrud /Lookahed anchors
(?=...) - positiivne ettevaatlus / positive lookahead assertion
(?!...) - negatiivne ettevaatlus / negative lookahead assertion
Tagasivaatavad ankrud /Lookbehind anchors
(?<=...) - positiivne tagasivaatlus
(?<!....) - negatiivne tagasivaatlus.
TAGASIVIITEID saab ankrute külge ka panna, selleks ümbritsega sulgude sees kasutatav regulaaravaldis uute sulgudega.
Nagu öeldakse, ümbervaatamise operatsioon ise ei salvesta regulaaravaldist, mis ümbervaaatluse operaatori taga on, see ei ole „capturing” expression.
Lambaid saab 'loomad' failist lugeda niimoodi:
grep -Po -e '-?\d+(?= lammas)|(?<=lambaid )-?\d+' loomad | awk '{ sum += $1 } END { print sum }'
saame 13 lammast
Analoogia põhjal jäneseid:
grep -Po -e '-?\d+(?= jänes)|(?<=jäneseid )-?\d+' loomad | awk '{ sum += $1 } END { print sum }'
vastus 10 jänest
grep -Po -e '-?\d+(?= siga)' loomad | awk '{ sum += $1 } END { print sum }'



annab 4 siga.


Siin ma praegu lõpetan, sest grep -P ei ole pikemas perspektiivis korralik töövahend perl ühilduvate regulaaravaldiste uurimisel. Piirduge grepi puhul grep -E võimalustega ja edasise arengu huvides omandage programmeerimiskeel perl.
Kogunenud bibliograafia:
1. Jeffrey Friedl „Mastering regular expressions”
http://regex.info
Peab olema number üks igasugueid regulaaravaldisi käsitlevas bibliograafias.
2. Jan Goyvaerts Regular-Expressions-Cookbook
http://www.amazon.com/Regular-Expressions-Cookbook-Jan-Goyvaerts/dp/1449319432
3. Rex Egg - meeldejääv, pühendumusega valminud netisait regulaarseist avaldistest
http://www.rexegg.com/
4. http://www.regular-expressions.info/
Jan Goyvaertsi netisait. Ühinen Rex Egg autori kiidusõnadega!
5. http://www.princeton.edu/~mlovett/reference/Regular-Expressions.pdf
Jan Goyvaertsi Princetonis kirjutatud ülevaade regulaarsetest avaldistest.
....
Tõenäoliselt lisan korralikuma bibliograafia perli osas hiljem.
Näpuotsaga eestikeelseid viiteid:
1. http://kuutorvaja.eenet.ee/programmeerimine/regulaaravaldised.html
2. https://wiki.itcollege.ee/index.php/Regulaaravaldis
3. http://viki.keeleleek.ee/wiki/Regulaaravaldised
Probleemiks on terminoloogia. Pakkusin siinsetes lugudes mõnedele terminitele vasteid välja, nii teeb aga igaüks. Ühtse terminoloogia puudumine aga teeb väga raskeks omavahelise arusaamise.
Inglise keel alati ei aita, sest me ei suhte päevad läbi inglise keeles.
.... Isiklikult tunnen puudust Ustus Agurist. Eestikeelne It terminoloogia ajalugu jaguneb laias laastus kaheks ajastuks:
Aguri aegne (agurismid!), kuni 1997
ja post Aguri aegne anarhia, kus me praegu asume.