Predavanje 16: Glava 42

FlipView[{Text[Style[" Branko Urošević, imqfbrankourosevic@gmail.com ",20,"Text",Italic]],Text[Style[" Faculty of Economics, University of Belgrade, NBS and CESIfo Munich",20,"Text", Italic]]}]

Dodatne informacije o stringovima i prepoznavanju oblika sa stringovima

Stringovi se koriste u velikom broju disciplina. One predstavljaju podatke, imena fajlova, kodove za financijske hartije od vrednosti i druge objekte. Koriste ih lingvisti, biolozi u proučavanju genoma, finansijski stručnjaci itd.

Ranije smo već videli neke od ovih funkcija. Sada želimo da istaknemo još neke važne principe rada sa stringovima, kao i da vam pokažem neke cool primene.

Kao što smo videli ranije, mnoge funkcije koje postoje za List manipulacije postoje i u String verziji. Ako vam padne na pamet neka funkcija za liste, probajte da ispred njenog naziva stavite String. Na primer, Join postaje StringJoin, Riffle postaje StringRiffle itd.

Sa druge strane, string funkcije funkcije često rade brže nego one sa listama. To je stoga što su stringovi jedan jedini tip podataka, a liste mogu  da se sastoje od raznorodnih podataka.

Karakteri

Stringovi su izrazi koji se sastoje od jednog broja karaktera koji se nalaze pod navodnicima “ “.  Karakteri mogu biti bilo šta što možete da otkucate na tastaturi uključujući velika i mala slova, brojevi, znaci interpunkcije, prazna polja i puno toga drugog. Evo skupa Asci karaktera koji se mogu odštampati

CharacterRange[" ","~"]

Drugi karakteri takođe postoje. Na primer, grčka slova mogu se otkucati na sledeći način. Slovo α se kuca kao: Esc a Esc.

CharacterRange["α","ω"]

CharacterRange["Α","Ω"]

Stringovi su atomi i ne mogu se u principu dalje deliti (iako se iz jednog stringa može praviti više njih, ali se to ne može ostvariti preko funkcije Part kao u slučaju izraza). Ali, to ne znači da se ne mogu predstaviti preko liste karaktera. Međutim, i karakteri su stringovi, te su to takođe atomi.

Part["This is a string",1]

Karakteri iz drugih jezika takođe mogu da se dobiju na ovaj način. Evo grčkih i japanskih karaktera:

FromCharacterCode[Range[913,937]]

FromCharacterCode[Range[30010,30030]]

Evo ovde malo ćirilica na raznim jezicima:

FromCharacterCode[Range[1032,1119]]

ToString i ToExpression

Stringovi su u outputu bez znaka navoda.

"The string"

Da bismo videli kako izgleda input forma za neki string koristimo InputForm:

InputForm[%]

Postoje funkcija koja konvertuje izraze u stringove,ToString, kao i funkcija koja konvertuje stringove u izraze, ToExpression:

ToString["a+1+3"]

ToExpression[%]

Šta će se desiti ako probamo da uradimo sledeći

(("102")^2)

Sa druge strane

((ToExpression["102"])^2)

Primena ToString i ToExpression za pravljenje varijabli

Pretpostavimo da želimo da napravimo seriju promenljivih (na primer za potrebe optimizacije) koje bi se zvale

var1, var2,...., var30

Kako to možemo najlakše uraditi? Pođimo od stringa “var” kao i stringova sa brojevima od 1 do 30. Spojimo ih i onda pretvorimo u varijable

Table[StringJoin["var",ToString[i]],{i,30}]

To bi bili stringovi Ako želimo varijable (simbole) onda te stringove transformišemo pomoću ToExpression

Table[ToExpression[StringJoin["var",ToString[i]]],{i,30}]

Testovi na stringovima

Postoji veliki broj funkcija koje testiraju da li se stringovi sastoje samo od slova (prvi primer), da li se sastoje samo od malih slova itd.

LetterQ["Here"]

LetterQ["Here we go"]

LowerCaseQ["Here"]

LowerCaseQ["strings"]

DigitQ testira da li su svi karakteri u nekom stringu cifre.

DigitQ[#]&/@{"12345","13.12","x123"}

Velika i mala slova

ToLowerCase sva slova u stringu piše malim slovima. ToUpperCase sva slova u stringu piše velikim slovima

{ToLowerCase["Here"],ToUpperCase["Here"]}

Napravite funkciju koja će da uzima string, pravi da je prvo slovo veliko a da su sva ostala mala. Kao primer na kome ćemo da je probamo jeste string  “StRIng”. Evo nekih važnih elemenata te funkcije: Podsetimo se da

Characters[ToLowerCase["StRIng"]]/.{x_,y__}:> StringJoin[{ToUpperCase[x],y}]

capitalize[inputstring_String]:=Characters[ToLowerCase[inputstring]]/.{x_,y__}:> StringJoin[{ToUpperCase[x],y}]

capitalize["StRIng"]

capitalize["StRIng and Us"]

Pretpostavimo da želimo da početno slovo bude malo a da ostala ostanu takva kakva su. Možemo to da uradimo ovako:

Characters["StRIng"]/.{x_,y__}:> StringJoin[{ToLowerCase[x],y}]

Prebacivanje teksta iz ćirilice u latinicu

celalatinica={"A","B","V","G","D","Đ","E","Ž","Z","I","J","K","L","Lj","M","N","Nj","O","P","R","S","T","U","F","H","C","Ć","Č","Dž","Š","a","b","v","g","d","đ","e","ž","z","i","j","k","l","lj","m","n","nj","o","p","r","s","t","u","f","h","c","ć","č","dž","š"};

celacirilica={"А","Б","В","Г","Д","Ђ","Е","Ж","З","И","Ј","К","Л","Љ","М","Н","Њ","О","П","Р","С","Т","У","Ф","Х","Ц","Ћ","Ч","Џ","Ш","a","б","в","г","д","ђ","е","ж","з","и","ј","к","л","љ","м","н","њ","о","п","р","с","т","у","ф","х","ц","ћ","ч","џ","ш"};

cirilicaToLatinica[str_String]:=StringJoin[Characters[str]//.MapThread[Rule,{celacirilica,celalatinica}]]

cirilicaToLatinica["Ја сам џивџан који воли да њушка. Да ми је само да сам мачка. Ништа не знам. Џамоња је супер вајар."]

Namerno sam ovo uradio ovakav luckast tekst, da bi imao što više potencijalnih problema

Prebacivanje teksa iz latinice u ćirilicu je nešto kompleksnije

MapThread[Rule,{celalatinica,celacirilica}]

Prvo pravimo generalizaciju funkcije Characters u slučaju naše latinice. Ovde je važno da napravimo funkciju koja razume da su nj, lj i takve stvari, zapravo jedno slovo a ne dva. Moramo da napravimo pravila za sva takva mala i velika slova.

latinicaCharacters[str_String]:=Characters[str]/.{x1___,"D","ž",y1___}:> {x1,"Dž",y1}//.{x2___,"d","ž",y2___}:> {x2,"dž",y2}//.{x3___,"N","j",y3___}:> {x3,"Nj",y3}//.{x4___,"n","j",y4___}:> {x4,"nj",y4}//.{x5___,"l","j",y5___}:> {x5,"lj",y5}//.{x6___,"L","j",y6___}:> {x6,"Lj",y6}

latinicaCharacters["Jа sаm dživdžаn koji voli dа njuškа. Dа mi je sаmo dа sаm mаčkа. Ništа ne znаm. Džаmonjа je super vаjаr."]

Sada nije teško da formulišemo funkciju koja prebacuje sa latinice na srpsku ćirilicu:

latinicaToCirilica[str_String]:=StringJoin[latinicaCharacters[str]//.MapThread[Rule,{celalatinica,celacirilica}]]

latinicaToCirilica["Jа sаm dživdžаn koji voli dа njuškа. Да mi je sаmo dа sаm mаčkа. Ništа ne znаm. Džаmonjа je super vаjаr."]

Osnovne operacije na stringovima

Pre nego što se otisnemo ka komplikovanijim stvarima, bilo bi korisno da se podsetimo i sistematizujemo znanje o osnovnim operacijama na stringovima. StringTake izdvaja deo stringa sa određenim brojem karaktera. Ovde ostavljamo prvih 12 karaktera.

StringTake["Three quarks for Muster Mark!",12]

Prethodno smo videli Characters i StringJoin. Oni su, na neki način, inverzne operacije.

Characters["quarks"]

StringJoin[%]

Jednostavan način da napišem StringJoin je pomoću <>

"x"<>"22"

Sledeća operacija imaju direktne analoge kod lista:

StringReverse["abcde"]

Primetimo da je StringReverse ekvivalentan tome da podelimo string na karaktere, uradimo Reverse i onda StringJoin. Naime

StringJoin[Reverse[Characters["abcde"]]]

Još operacija na stringovima

Slično, StringDrop je

StringDrop["abcde",-1]

To je ekvivalnetno

StringJoin[Drop[Characters["abcde"],-1]]

StringPosition["I am Sam. Sam I am","am"]

StringPosition["I ami Sami. Sami I ami","ami"]

Primetimo da StringPosition daje početnu i krajnju poziciju za svaku podsekvencu karaktera. StringCount broji broj puta koliko se određena grupa karaktera pojavljuje u stringu koji testiramo:

StringCount["I ami Sami. Sami I ami","ami"]

StringInsert ubacuje neki string u zadatu poziciju.

StringInsert["abcde","TATA",3]

Umesto da ubacujemo možemo da zamenimo neki deo sa nečim drugim pomoću StringReplace:

StringReplace["I am Sam. Sam I am","am"-> "ami"]

Napravite listu svih mogućih reči iz neke azbuke (odnosno liste karaktera)

Napravimo funkciju koja uzima neke karaktere i pravi od njih sve moguće reči dužine 2. Razmotrimo četiri osnovna sastojka DNK (adenin, timin, citozin, guanin):

alphabet={"A","T","C","G"};

Zanimaju nas kombinacije (reči) koje možemo da napravimo od ovih slova. Jedan način da formiramo sve moguće reči dužine 2 jeste:

Table[i<>j,{i,alphabet},{j,alphabet}]

i da onda poravljamo:

Flatten[Table[i<>j,{i,alphabet},{j,alphabet}]]

Na taj način, funkcija koja kreira sve kombinacije dve osnovne nukleinske kiseline je:

twoLetterWords[alphabet_List]:=Flatten[Table[i<>j,{i,alphabet},{j,alphabet}]]

twoLetterWords[alphabet]

To radi. Drugi način da napravimo to isto je preko funkcije Tuples. Ona pravi sve moguće uređene parove određene dužine.  Sve što treba da uradimo onda jeste da spojimo svaku dobijenu listu u jedan string. U slučaju dužine dva:

Nastavak sa pravljenjem svih reči na osnovu neke azbuke

Tuples[alphabet,2]

U slučaju dužine tri:

Tuples[alphabet,3]

Da bismo spojili slova u jednu reč koristimo Map za funkciju StringJoin na Tuples

Map[StringJoin,Tuples[alphabet,2]]

Evo nove verzije funkcije

nLetterWords[alphabet_List,n_Integer]:=Map[StringJoin,Tuples[alphabet,n]]

nLetterWords[alphabet,2]

nLetterWords[alphabet,3]

Stvoriti randomStrings za zadatu azbuku (listu karaktera)

Prepostavimo da imamo ista 4 slova kao i ranije i da hoćemo da pravimo slučajne stringove određene dužine od elemenata te azbuke. Sve što treba da uradimo jeste da primenimo RandomChoice na prethodno dobijeni skup mogućih reči. Ovde je n dužina slučajne reči dok je m broj slučajnih reči.

randomStrings[alphabet_List,n_Integer,m_Integer]:=RandomChoice[Map[StringJoin,Tuples[alphabet,n]],m]

Ovo daje 30 slučajnih parova i tripleta DNK segmenata.

randomStrings[alphabet,2,30]

randomStrings[alphabet,3,30]

Sada možemo da simulišemo sekvence DNK tako što napravimo veliki broj parova i onda ih spojimo.

randomDNA=StringJoin[randomStrings[alphabet,2,1000]];

StringLength[randomDNA]

Podsetimo se da su ovo svi moguću parovi koje možemo da napravimo od 4 slova:

twolettersequence=nLetterWords[alphabet,2]

Analiza strukture randomDNA

Koliko puta se pojavio “AA” u randomDNA?

StringCount[randomDNA,"AA"]

To možemo da to uradimo za svaki mogući par:

occurencies=StringCount[randomDNA,#]&/@twolettersequence

A ovako možemo da napravimo Histogram. Prvo nađemo relativne frekvence:

freq=N[occurencies/Total[occurencies]]

Sada to možemo da predstavimo u BarChart-u:

BarChart[freq,BarSpacing->{0.5,0},ChartLabels->Placed[twolettersequence,Axis],ChartStyle->"DarkRainbow"]

Ovde  BarSpacing kontroliše razmake između susednih pravougaonika, a ChartLabels kontroliše natpise za svaki pravougaonik.

Operacije sa stringovima se često mogu uraditi preko operacija na listama

Pretpostavimo da hoćemo da nađemo broj puta koliko se slovo B pojavljuje u Darwinovom On the Origin of Species.

text=ExampleData[{"Text","OriginOfSpecies"}];

Primetimo da ovaj tekst ima preko 89000 karaktera:

StringLength[text]

Evo početka teksta:

StringTake[text,200]

Koliko puta se u tekstu pojavljuje veliko slovo “B”? To radimo na dva načina. Jedan je da razbijemo tekst na karaktere i da koristimo Count. Drugi je da koristimo specijalizovanu funkciju StringCount. U oba slučaja ćemo izmeriti vreme za te operacije:

Count[Characters[text],"B"]//Timing

StringCount[text,"B"]//Timing

Vidimo da StringCount radi brže. To je zato što je ta funkcija optimizovana da radi na stringovima. Ovako možemo da vidimo koliko puta se pojavljuje reč “selection” u tekstu.

StringCount[text,"selection"]

Šifrovanje poruka (encode): metod Julija Cezara

Šifrovanje i dešifrovanje teksta počelo je još u vreme Mesopotamije. Trgovci su želeli da čuvaju tajne vezane za recepte u vezi sa pravljenjem glazura na keramici.  Julije Cezar je napravio prost cipher (koder) za potrebe svoje vojske. Razmotrimo samo mala slova na engleskom.

alphabet=CharacterRange["a","z"]

Pretpostavimo da je on pomerio na levo svako slovo za po jedno mesto.

RotateLeft[alphabet]

Onda je koder sledeće pravilo.

CaeserCodeRule=Thread[alphabet->RotateLeft[alphabet]]

Da biste kodirali neki string, razbijte ga na individualne karaktere, primenite pravilo na svaki karakter, i onda ponovo spojte u string. U slučaju reči “hello”.

StringJoin[Characters["hello"]/.CaeserCodeRule]

Evo funkcije koja vrši kodiranje na osnovu pravila coderules.

encode[str_String,coderules_]:=StringJoin[Characters[str]/.coderules]

Razmotrimo kako bi izgledao kod za “squeamish ossifrage”

encode["squeamish ossifrage",CaeserCodeRule]

Dekodiranje

Da bismo dekodirali, moramo da napravimo reverzno mapiranje:

CaeserDecodeRule=Map[Reverse,CaeserCodeRule]

Funkcija za dekodiranje je

decode[str_String,decoderules_]:=StringJoin[Characters[str]/.decoderules]

decode["trvfbnjti pttjgsbhf",CaeserDecodeRule]

Jasno je da je ovo nebezbedno i da nije teško da se ukapira kako to radi. Jedan način da se to drugačije napravi jeste da se slova promućkaju:

p=RandomSample[alphabet]

randomencode=Thread[alphabet-> p]

Tako da bi naš kod sada bio

encode["squeamish ossifrage",randomencode]

Ako ga invertujemo

randomdecode=Map[Reverse,randomencode]

decode["xreofjtxd sxxtzlfbo",randomdecode]

Anagrami

Anagrami su reči koje se sastoje od istih karaktera ali u drugačijem poretku. Anagrami se mogu programirati tako što uzmemo reč, razbijemo je na karaktere, i nađemo sve moguće permutacije karaktera i najzad odredimo koje od njih postoje u rečniku.

chars=Characters["tame"];

Permutations[chars]

words=Map[StringJoin,Permutations[chars]]

Najzad, treba da nađemo koje od ovih reči su u rečniku. IgnoreCase-> True opcija kaže da treba da ignorišemo da li su reči napisane velikim ili malim slovom.

Flatten[DictionaryLookup[#,IgnoreCase->True]&/@words]

Evo funkcije koja pravi anagrame

Anagrams[initword_String]:=Module[{chars=Characters[initword],words},words=Map[StringJoin,Permutations[chars]];Flatten[DictionaryLookup[#,IgnoreCase->True]&/@words]]

Anagrams["parsley"]

Anagrams["elvis"]

String oblici (prvi deo)

Mnoge operacije na stringovima koje smo do sada koristili upotrebljavaju bukvalne stringove. Na primer, u StringReplace mi smo eksplicitno specifikovali kako string na kome radimo tako i šta ćemo sa čim zameniti:

StringReplace["11/28/2015","/"-> "-"]

Ovako možemo da permutujemo mesece i dane, to jest da prevedemo iz američkog u evropski način pisanja datuma:

StringReplace["11/28/2015",StringExpression[x__,"/",y__,"/",z__]:> StringJoin[y,"/",x,"/",z]]

Ovako kombinujemo i jedno i drugo.

StringReplace[StringReplace["11/28/2015",StringExpression[x__,"/",y__,"/",z__]:> StringJoin[y,"/",x,"/",z]],"/"-> "-"]

Međutim, ono što je posebno kul u vezi sa string oblicima (string patterns) jeste da možemo da radimo moćnije stvari od toga. String oblik je izraz koji sadrži simboličke oblike. Puno toga što smo već učili o oblicima primenjuje se direktno na stringove. Na primer, evo kako možemo da koristimo oblike da promenimo prvo slovo u veliko slovo.

str="this is my only regret!";

StringReplace[str,f_~~rest__:> ToUpperCase[f]<>rest]

Ovo možemo da predstavimo i u punoj formi:

Nastavak o oblicima stringoa

StringExpression[f_,rest__]

StringReplace[str,StringExpression[f_,rest__]:> StringJoin[ToUpperCase[f],rest]]

Ovde je f_~~rest__ skraćenica za StringExpression[f_,rest__] što, sa tačke gledišta matchovanja oblika sekvenca stringova.

Primetimo da je

"a"~~"b"

isto što i

StringExpression["a","b"]

StringExpression je veoma sličan StringJoin. Oba spajaju stringove. Ali sa StringExpression možete da spajate i patterne i klase stringova, ne samo ekplicitne stringove! StringJoin radi samo sa eksplicitnim stringovima.

Primene StringExpression u radu sa oblicima stringova

Možemo da koristimo uslovni oblik da proverimo da li je prvi karakter veliko slovo:

StringMatchQ[str,StringExpression[f_?UpperCaseQ,rest___]]

StringMatchQ["This is my only regret!",StringExpression[f_?UpperCaseQ,rest___]]

Ovo možemo i ovako da to odradimo:

StringMatchQ["This is my only regret!",f_?UpperCaseQ ~~rest___]

Da bismo počeli, razmišljajmo o stringovima kao o sekvenci karaktera i onda radimo kao i sa listama. Ovaj oblik daje match pošto postoje dva uzastopna elementa liste koja su ista, a pre toga bar još jedan drugi element:

MatchQ[{a,b,c,c,e,f},{__,s_,s_,__}]

Analog kod stringova bi bio:

StringMatchQ["abccef",__~~s_~~s_~~__]

Evo i u FullForm

StringMatchQ["abccef",StringExpression[__,s_,s_,__]]

Upoređenje Cases sa StringCases

Podsetimo se da Cases mogu da se koriste, na primer, da bismo izdvojili one elemente liste koji imaju isti head. Na primer, ovako izdvajamo samo simbole.

Cases[{1,f,g,3,12,f21},_Symbol]

Kod stringova, StringCases ima kao prvi argument neki string, a kao drugi argument string oblik koji predstavlja klasu karaktera koju treba matchovati. To onda vraća one podstringove koji imaju takav oblik. Na primer:

StringCases["1fgxt25",LetterCharacter]

Evo još jednog načina da se uradi to isto.

StringCases["1fgxt25",_?LetterQ]

StringCases["1fgxt25",DigitCharacter]

StringCases["1fgxt25",NumberString]

Razmotrimo sledeću rečenicu i naći listu karaktera koji se sastoje od “sam” posle koga dolazi jedan karakter.

tekst="Ja sam sama na ovom svetu.";

StringCases[tekst,StringExpression["sam",_]]

Prvi element liste nalazi prvo “sam” i dodaje jedno prazno mesto posle toga (sledeći karakter), dok je druga nalaženje reči “sam” plus karakter “a”, to jest reči “sama”.

Primena StringCases za analizu podataka o ljudskom genomu

Da bismo bolje razumeli opštost string oblika, razmotrimo gene za nukleotidsku sekvencu koja sadrži AA, pa proizvoljan karakter, pa onda T. Evo informacije o konkretnoj genetskoj strukturi ljudskog genoma.

gene=GenomeData["IGHV357"]

StringLength[gene]

StringCases[gene,StringExpression["AA",_,"T"]]

Pretpostavimo da želimo da nađemo sve strukture gde se pojavila struktura prvo “GTC” a onda posle toga nešto. Evo načina da se to uradi:

StringCases[gene,pat:StringExpression["GTC",x_]:> StringJoin[pat,x]]

Na taj način pattern koji se zove pat je string GTC posle koga sledi proizvoljan jedan karakter koji se zove x. To što smo uveli ime x omogućava nam da se posle na to pozovemo kad definišemo sa desne strane pravila šta treba da uradimo. Naime, treba da spojimo prvo sledeće slovo sa GTC tamo gde ta kombinacija postoji.

Pretpostavimo da hoćemo 5-slovne reči u kojim se GTC usendvičeno sa po jednim karakterom:

StringCases[gene,StringExpression[x_,"GTC",y_]:> StringJoin[x,"GTC",y]]

Scrapping (preuzimanje sa Web-a) telefonskih brojeva

Treba da konstruišete oblik koji se matchuje kod telefonskih brojeva. U ovom slučaju to je n-nnn-nnn-nnnn (to uključuje country code za Ameriku, koji je 1, kao i trocifreni area code. Evo kako to možemo da uradimo:

webpage=Import["http://www.wolfram.com/company/contact","HTML"];

StringCases[webpage,StringExpression[NumberString,"-",NumberString,"-",NumberString,"-",NumberString]]

StringTrim je jedna od funkcija specifična za stringove. Ona skraćuje stringove da im da lepši oblik. U ovom primeru, ako nađemo “http:// ili /” to odbacimo (ovo su string patterni koje ćemo kasnije proučavati).

StringTrim["http://www.google.com/","http://"| "/"]

Naći podnizove u nizu brojeva pomoću stringova

Jedna od prednosti rada na stringovima jeste da su brzi. Pogledajmo kako možemo da koristimo operacije na stringovima kao alternativu operacijama na listama. Na primer, želimo da nađemo podniz velikog niza brojeva. Evo kako to možemo da uradimo putem konverzije u stringove i nazad.

num=ToString[N[π,100]]

StringSplit[num,"."]

Izaberimo deo posle decilane tačke

StringSplit[num,"."][[2]]

Nađimo pozicije gde se pojavljuje “3238”

StringPosition[StringSplit[num,"."][[2]],"3238"]

Sada napravimo od toga funkciju:

findSequence[num_?NumberQ,subseq_?NumberQ]:=Module[{n=ToString[num],s=ToString[subseq]},StringPosition[StringSplit[n,"."][[2]],s]]

Probajmo na velikom broju:

pi=N[π,(10^7)];

findSequence[pi,314159]//Timing

Alternative

Mi smo već videli kako da radimo sa alternativama kod opštih oblika. One se mogu koristiti i kod string oblika. Na primer, često se kod analize genoma traži da se nađe racio GC odnosno koliko ima (G) ili (C) u odnosu na sve četiri DNK baze. Evo konkretnog gena.

gene=GenomeData["MRPS35P1"];

Ono što možemo da uradimo jeste:

StringCount[gene,"G"]+StringCount[gene,"C"]

Ili možemo da koristimo alternative (označene sa |)

StringCount[gene,"G"|"C"]

Ista ideja je u slučaju kada u tekstu hoćemo da nađemo koliko se puta spominje, na primer, Cyclops u Homerovoj Odiseji. On se pojavljuje i kao Polyphemus.

odyssey=Import["http://www.gutenberg.org/ebooks/1727.txt.utf-8","Text"];

StringCount[odyssey,"Cyclops"|"Polyphemus"]

Ali postoji još jedno ime, “Cyclopes”. Stoga:

StringCount[odyssey,"Cyclops"|"Cyclopes"|"Polyphemus"]

Upoređenje Cases i StringCases

Primetimo da StringCases radi na stringovima. Ako imate više stringova, ići će kroz listu i ako ne naiđe na match daće prazan skup. Sa druge strane, Cases daje samo one elemente koje je matchovao. Evo primera:

StringCases[{"corner","rever","pitch"},w__/;(w==StringReverse[w] && StringLength[w]>1)]

Ova funkcija je prošla kroz listu stringova i izdvojila one koje su dužine veće od jedan i koji su palindromi (to jest, za njih je reverzna reč jednaka samoj sebi). Nešto slično može da se uradi i sa Cases:

Cases[{"corner","rever","pitch"},w__/;(w==StringReverse[w] && StringLength[w]>1)]

Razlika je još veća ako ne stavimo drugi uslov. U tom slučaju:

StringCases[{"corner","rever","pitch"},w__/;w==StringReverse[w] ]

Ovo se događa je se sada svaki karakter može da smatra palindromom (drugi uslov je to sprečavao). Sa druge strane

Cases[{"corner","rever","pitch"},w__/;w==StringReverse[w] ]

Razlog je što Cases, suprotno StringCases, ne razbija interno stringove u karaktere nego gleda na njih kao celinu.

Funkcija koja nalazi palindrome određene dužine

Evo kako možemo da napravimo funkciju koja pretražuje reči iz rečnika i nalazi one koje su identične svojih reverznim i imaju određenu dužinu n. Primetimo da je važno da koristimo BlankSequence (__) jer bi prost pattern _ dao samo reči dužine 1.

palindromes[len_Integer]:=DictionaryLookup[w__/;(w==StringReverse[w] && StringLength[w]==len)]

palindromes[7]

Više o string oblicima

String oblici rade vrlo slično ostalim oblicima u Wolfram Language, osim što rade specifično sa sekvencama karaktera u stringovima. U string oblicima, možemo da kombinujemo konstrukcije tipa  _ sa stringovima tipa "abc" koristeći ~~. U ovom primeru, dobijamo sve slučajeve kod kojih posle + dolazi jedan karakter:

StringCases["+string +patterns are +quite +easy", StringExpression["+",_]]

Ovo daje prva tri karaktera posle svakog +:

StringCases["+string +patterns are +quite +easy", StringExpression["+",_,_,_]]

Ako koristimo ime x (imenovani oblik) za karakter posle svakog +, možemo da vratimo karakter uramljen:

StringCases["+string +patterns are +quite +easy", StringExpression["+",x_]->Framed[x]]

U string patternu, _ odgovara proizvoljnom jednom karakteru. _ _ (“double blank”) odgovara proizvoljnoj sekvenci jednog ili više karaktera, i _ _ _ (“triple blank”) odgovara proizvoljnoj sekvenci nula ili više karaktera. _ _ kao i _ _ _ normalno uhvate koliko god mogu više od stringa. Evo kako da izaberemo sekvencu karaktera unutar uglaste zagrade [  ]:

StringCases["the [important] word", StringExpression["[",x__,"]"]->Framed[x]]

_ _ normalo matchuje najdužu moguću sekvencu karaktera:

StringCases["now [several] important [words]", StringExpression["[",x__,"]"]->Framed[x]]

Nastavak o oblicima stringova

Shortest ga natera da izabere najkraću sekvencu karaktera koju može da matchuje:

StringCases["now [several] important [words]",StringExpression[ "[",Shortest[x__],"]"]->Framed[x]]

StringCases bira one delove stringa koji odgovaraju određenom patternu. StringReplace pravi zamene (mada i u StringCases kao što smo videli, možemo da pravimo zamene). Zameni sledeće karaktere u stringu:

StringReplace["now [several] important [words]", {"["->"<<","]"->">>"}]

Napravite zamene za oblike koristeći  :> da izračunateToUpperCase u svakom od slučajeva za koji postoji match:

StringReplace["now [several] important [words]", StringExpression["[",Shortest[x__],"]"]:>ToUpperCase[x]]

Koristi NestList da primeniš string replacement (zamenu) više puta:

NestList[StringReplace[#,{"A"->"AB","B"->"BA"}]&,"A",5]

StringMatchQ testira da li string matchuje neki pattern. Select reči koje matchuju pattern da počinju na  a i završavaju na b:

Select[DictionaryLookup[],StringMatchQ[#,StringExpression["a",___,"b"]]&]

A sada tražimo reči koje počinju sa “a”, onda imaju tačno četiri karaktera i na kraju je “b”.

Select[DictionaryLookup[],StringMatchQ[#,StringExpression["a",_,_,_,_,"b"]]&]

Rad sa rečnikom

Šta dobijemo ukoliko probamo da nađemo sve reči u DictionaryLookup[] koje počinju na “q” i imaju 5 slova:

Select[DictionaryLookup[],StringMatchQ[#,StringExpression["q",_,_,_,_]]&]

Možete da koristite alternative | kao i ponovljeni pattern .. u string patternima kao i u običnim. Izaberi bilo koju sekvencu A ili B koja može da se ponavlja:

StringCases["the AAA and the BBB and the ABABBBABABABA",("A"|"B")..]

Uporedi sa sledećim: naći sekvence gde se A pojavljuje i-ili ponavlja više puta uzastopno

StringCases["the AAA and the BBB and the ABABBBABABABA","A"..]

U string oblicima, LetterCharacter je bilo koje slovo, DigitCharacter je bilo koja cifra (ali kao deo stringa), i Whitespace je bilo koja sekvenca karaktera kao što su prazna polja. Izabrati sekvence DigitCharacter (cifara):

StringCases["12 and 123 and 4567 and 0x456",DigitCharacter..]

Izabrati sekvence cifara oko kojih je WhiteSpace:

StringCases["12 and 123 and 4567 and 0x456",StringExpression[Whitespace,DigitCharacter..,Whitespace]]

StringSplit, StringJoin i StringRiffle

Često je u praksi uobičajeno da stringove pretvorimo u listu i obrnuto. String možete pretvoriti u listu sastavnih delova, ali ne i karaktera, pomoću StringSplit. Po defoltu, break je kad imamo prazninu:

StringSplit["a string to split"]

Ovde koristimo string pattern da odlučimo gde ćemo da razbijemo string:

StringSplit["you+can+split--at+any--delimiter","+"|"--"]

Postoji specijalni newline character (predstavljan sa \n) koji pokazuje gde string treba da se prekine i da ide u novi red. Razbijmo string kod newlines:

StringSplit["first line
second line
third line","\n"]

Spojiti stringove ubacivši između svakog string "---" (StringRiffle ubacujemo taj string):

StringRiffle[{"a","list","of","strings"},"---"]

Često je zgodno napraviti string od proizvoljnog izraza u WL. TextString pretvara brojeve i druge izraze u stringove:

StringJoin["two to the ",TextString[50]," is ",TextString[2^50]]

Šabloni stringova (StringTemplate)

Često je jednostavnije praviti stringove iz izraza korišćenjem šablona. Šabloni rade kao čiste funkcije u tom smislu da su u pitanju slots (mesta) u koja se nešto može ubaciti. U šablonu svaki `` je slot za sukcesivni argument:

StringTemplate["first `` then ``"][100,200]

Imenovani slotovi biraju elemente iz asocijacije:

StringTemplate["first: `a`; second `b`; first again `a`"][\[LeftAssociation]"a"->"AAAA", "b"->"BB BBB"\[RightAssociation]]

Možete da ubacite bilo koji izraz unutar string template-a tako što ga stavite u  <*...*>. Vrednost izraza se izračuna kada se template primeni. Evo jednog primera:

StringTemplate["2 to the 50 is <* 2^50 *>"][ ]

Koristi slots u templateu (` is the backquote character):

StringTemplate["`1` to the `2` is <* `1`^`2` *>"][2,50]

Ovaj izraz u templateu je izračunat kada se template primeni:

Table[StringTemplate["`1` to the `2` is <* `1`^`2` *>"][i,j],{i,2,4},{j,10,30,10}]//Grid

StringTemplate["the time now is <* Now *>"][ ]