SAŽETAK: Program KaMP nalazi parove reči koje su na segmentalnom nivou govora nepodudarne samo po dvama odabranim faktorima (Deza and Deza 2016, 215) dužine bar po 1, npr. pet ~ peć, filma‚ ~ firma, ist`orizovati ~ ma - j`orizovati, p‚esničk¯ ~ pol`itičk¯. 
Predmet rada su brže varijante KaMP-a sa poboljšanim sortiranjem sa suplementarnim modom. 
KLjUČNE REČI: fonetika, fonologija, obrada prirodnog jezika, korpusna lingvistika, Python. 
Prema (Bugarski 2003, 128), minimalni parovi su parovi „kod kojih se dve po značenju odelite reči formalno razlikuju samo u jed-noj fonemi“, npr. b‚as ~ č‚as. 
Program Ka minimalnim parovima (Aleksić and Šandrih 2021) u srpskom korpusu nalazi parove reči uzajamno formalno različitih samo po zadatim podniskama, pri čemu zanemaruje prozodiju i to da li su slova velika ili mala. 
Korpus treba da bude kodiran shemom UTF-8. 
Osim zadatih podniski, u „rečima“ mogu biti (I) karakteri od	do	, od	do	i od	´	do	u "A"	"Z"	"a"	"z"	"C"	"ž" odgovarajućim tabelama Unicode-a i (II) crtice u medijalnom položaju. 
Sadržaj ulazne datoteke  Zadate podniske Niska za izlaz              "Klima-uređaji pre  klima-uređaja"            "a", "i"                      "klima-uređaja ~ Klima-uređaji" (or "Klima-uređaji~klima-uređaja")                                "α-čestica, α-čestice, α-čestici"               "a", "e"                          "čestica ~ čestice"             "α-čestica,β-čestica"                           "α", "β"                            "α-čestica ~ β-čestica" 
Tabela 1. KaMP: primeri ulaza i izlaza 
Prezentovani program može biti od koristi profesorima srpskog kao stranog jezika i lingvistima (Aleksić and Šandrih 2021, 574–75). 
Oblast Zadate podniske Utilizacija              Nastava srpskog kao stranog jezika "c", "č" Temelj pitanja u vežbi:  „C ili č?             1) Šta bi ti uradio, dragi čitao_e? Naše novine će poštovati svoje čitao_e.             2) [...]“              Derivatologija "auto", "samo" Podaci o konkurenciji  segmenata auto- i samo-. 
Tabela 2. KaMP: primeri utilizacije 
Ovom prilikom autori objavljuju i komentarišu unapređene verzije KaMP-a koje su izgradili, KaMP 2 i KaMP 2.1. 
KaMP 2 i KaMP 2.1 alati su iz domena obrade prirodnog jezika ako se ona shvati „u širokom smislu“, na taj način da podrazumeva „bilo koju vrstu računarske manipulacije prirodnim jezikom“ (Bird, Klein, and Loper 2009, ix). Naime, dotični programi ne akcentuju ni mali broj svih srpskih reči, nego srpski jezik procesiraju površno. 
Budući da su donekle podešeni za pretragu obimnih korpusa, novi KaMP-ovi su skromni prilozi i korpusnoj lingvistici ako se ona definiše npr. kao „računarski potpomognuta analiza vrlo opsežnih zbirki transkribovanih govornih jedinica ili pisanih tekstova“ (McEnery and Hardie 2012, i). 
KaMP 2 i KaMP 2.1 kodirani su u Python-u 3.8.2 (Python 2021a). 
Python je interpretirani viši programski jezik opšte namene (Pajankar 2020, 52). 
Ovaj jezik je „i elegantan i pragmatičan, i jednostavan i moćan“; „pogodan je za početnike u programiranju i odličan za stručnjake“ (Martelli, Ravenscroft, and Holden 2017, ix). 
Python postaje sve popularniji, i u 2017. godini postao je najpopularniji jezik na svetu prema magazinu IEEE Spectrum (Shovic and Simpson 2021, 1). 
Python je najšire upotrebljavani programski jezik u obradi prirodnog jezika (Anti´c 2021, vii). 
Može se očekivati da Python bude spor u poređenju sa kompajliranim jezicima, ali je on brži ako se u obzir ne uzme samo koliko traje izvršenje koda nego i koliko traje razvijanje koda (Unpingco 2021, 2). 
Python je u kasnim 80-im godinama prošlog veka stvorio holandski programer Gvido van Rosum (Cicolani 2021, 41; Rajagopalan 2021, 1). 
U (Aleksić and Šandrih 2021, 569) navedena su četiri alata za nalaženje minimalnih parova odn. „fonoloških suseda“ (Mairano and Calabr`o 2016, 258) koja su napravljena pre KaMP-a. 
Njima se mogu pribrojati programski paket za Python 3 Minpair (PyPI 2021) i kratki program na Python-u 2.7 sa stranice (Stack Overflow 2021a). 
Minpair po najmanje dvama zadatim „vokalskim fonološkim elementima“ traži „minimalne parove (i minimalne skupove) [samo jednosložnih — D. A.] reči iz američkog engleskog“, (A) defaultdict-om grupišući te reči prema priključenim transkripcijama u kojima je (B) zadate vokale, pomoću regularnog izraza i funkcije enumerate(), prethodno zamenio tačkom. 
Pristup A ima načelnu paralelu u KaMP-u 2.1, ali (prema Dodatku 2) nešto efikasniju. 
KaMP 2.1 reči uparuje pomoću standardnog rečnika (v. Dodatak 1). 
Pristup B ima načelnu paralelu u KaMP-u 2 i KaMP-u 2.1, ali (prema Dodatku 3) mnogo efikasniju. 
KaMP 2 i KaMP 2.1 zadate elemente specijalnom niskom zamenjuju pomoću metoda str.replace() i str.format() (v. Dodatak 1). 
Unos iz cmudict-a Torka za grupisanje ako su zadati vokali "AE" i "OW"              ("cat", ["K", "AE1", "T"]) ("coat", ["K", "OW1", "T"]) ("K", ".", "T") 
Tabela 3. Minpair: primer ulaza i torke za grupisanje 
1 # Minpair: primeri upotrebe 1 i 2             2 import minpair             3 print ( minpair . vowel_minpair (["AO", "ER"]) [12:13])             4 # Izlaz: [{’AO’: ’saw’, ’ER’: ’sir’}]             5             6 print ( minpair . vowel_minpair (["AA", "AO", "EH"]) [6:7])             7 # Izlaz: [{’AO’: ’dawn’, ’EH’: ’den’, ’AA’: ’don’}] 
Mogu se zadati i vrsta reči ili vrste reči. 
1 # Minpair: primeri upotrebe 3, 4 i 5             2 import minpair             3 print ( minpair . generator (pos =["ADV"]). vowel_minpair (             4 ["AH", "EH"]))             5 # Izlaz: [{’AH’: ’once’, ’EH’: ’whence’}]             6             7 print ( minpair . generator (             8 pos =[" ADJ", " VERB "]). vowel_minpair (             9 ["AE", "IH"]) [:1])             10 # Izlaz: [{’AE’: ’bad’, ’IH’: ’bid’}]             11             12 print ( minpair . generator (             13 pos =[" ADJ", " VERB "]). vowel_minpair (             14 ["AE", "IH"]) [22:23])             15 # Izlaz: [{’AE’: ’sang’, ’IH’: ’sing’}] 
Izvor(e) reči nije moguće zadati. Minpair „zavisi od nekolikih korpusa sa platforme NLTK“: „brown, cmudict, universal_tagset i words“. 
Kodom ponuđenim na stranici (Stack Overflow 2021a) uparuju se niske koje se razlikuju po jednom karakteru, a iste su dužine. 
Niske moraju biti unutar npr. liste, ali se pred njih ne postavljaju nikakvi prirodnojezički uslovi (nije nužno da one budu iz određenog jezika, odnosno na određenom pismu, pa ni da budu sačinjene od alfabetskih karaktera). 
Tokom izvršenja koda, svaka zadata niska poredi se karakter po karakter sa svakom zadatom niskom iza nje. 
Ako se poklapaju svi karakteri osim jednog, dotični par niski se ispisuje. 
1 # (Stack Overflow 2021a)             2 for n1 , word1 in enumerate ( wordlist ):             3 for word2 in wordlist [n1 +1:]:             4 if len( word1 )== len ( word2 ):             5 ndiff =0             6 for n, letter in enumerate ( word1 ):             7 if word2 [n]!= letter :             8 ndiff +=1             9 if ndiff ==1:             10 print word1 , word2             11             12 """Program sa stranice (Stack Overflow 2021a): primer upotrebe 1             13 (dodao D. A.)             14 Ulaz: ["kula", "kule", "kuli", "kulom"]             15 Izlaz:             16 kula kule             17 kula kuli             18 kule kuli             19 """ 
1 """Program sa stranice (Stack Overflow 2021a): primeri upotrebe 2 i 3             2 Ulaz: ["kula", "kulE", "kuli", "kulom"]             3 Izlaz:             4 kula kulE             5 kula kuli             6 kulE kuli             7             8 Ulaz: ["kula", "Kule", "kuli", "kulom"]             9 Izlaz:             10 kula kuli             11 """ 
Kada je u ulaznoj listi prva niska bila duplirana, u izlazu se javio dupliran par. 
1 """Program sa stranice (Stack Overflow 2021a): primer upotrebe 4             2 Ulaz: ["kula", "kula", "kule", "kulom"]             3 Izlaz:             4 kula kule             5 kula kule             6 """ 
Minpair (Stack         Overflow         2021a)         KaMP 2 i         KaMP 2.1         Izlaz su parovi niski         različitih po bilo kom         karakteru na datoj poziciji.         × ✓ ×         Diferencijalni elementi se         zadaju.         ✓ × ✓         Broj zadatih diferencijalnih         elemenata ne mora biti 2.         ✓ ×         Zadati diferencijalni         elementi ne moraju biti vokali.         × ✓         Reči ne mora diferencirati         niz od samo jedne foneme odn.         od samo jednog karaktera.         × × ✓         Ulaz bira korisnik. × ✓ ✓         Ulaz ne mora biti već         tokenizovan.         × ✓ 
Tabela 4. KaMP 2 / KaMP 2.1 spram srodnih alata 
U pripremnom delu algoritma formiraju se torke za poredbu reči, npr. ("Avali", "avali", "" v li"), ("Požeškom", "požeˇskom", "pož ˇskom"), ("sekretarijata", "sekretarijata", "s kr t rij t "). 
Prvi član torke za poredbu jeste reč koja sadrži jednu ili obe zadate podniske. 
Drugi član je prvi član prebačen u mala slova. 
Treći član je drugi član u kom je bar jedna potvrda prve ili druge zadate podniske zamenjena niskom "" ". 
Jednoj ekscerpiranoj reči mogu odgovarati jedna reč sa zamenom zadatih podniski (v. Tabelu 5) ili, (I) kada se zadate podniske preklapaju (Lothaire 2005, 7) ili (II) kada je jedna zadata podniska prava podniska (B¨ockenhauer and Bongartz 2007, 24) druge zadate podniske, više reči sa zamenom zadatih podniski (v. Tabelu 6). 
Niske za poredbu             Ekscerpirana             reč             Ekscerpirana reč             prebačena u             mala slova             Ekscerpirana reč             prebačena u mala             slova uz zamenjivanje             zadatih podniski             "Knjiga" "knjiga" "knjig "             "knjigu" "knjigu" "knjig "             "sveska" "sveska" "svesk "             "SVESKU" "svesku" "svesk "             "računaljku" "računaljku" "r č n ljk " 
Tabela 5. KaMP 2 / KaMP 2.1: primeri niski za poredbu (ako su zadate podniske "a" i "u") 
Niske za poredbu             Ekscerpirana             reč             Ekscerpirana reč             prebačena u mala             slova             Ekscerpirana reč             prebačena u mala             slova uz zamenjivanje             jedne zadate podniske             "ONA" "ona" "on "             "Onima" "onima" "on "             "Onima" "onima" "onim "             "onimima" "onimima" "onim "             "onimima" "onimima" "onimim  
Tabela 6. KaMP 2 / KaMP 2.1: primeri niski za poredbu (ako su zadate podniske "a" i "ima") 
U glavnom delu algoritma formirane torke se porede. 
KaMP 2 generiše sve moguće dvočlane kombinacije torki sa prvom zadatom podniskom i torki sa drugom zadatom podniskom i onda preskače neželjene kombinacije. 
Moguće dvočlane kombinacije ovaj program dobija tako što izračunava Dekartov proizvod pomenutih dveju grupa torki. 
KaMP 2.1 od torki sa drugom zadatom podniskom obrazuje heš-mapu (tabelu) i proverava da li se u njoj sreću reči sa zamenom uzete iz torki sa prvom zadatom podniskom. 
Ključ mape je reč sa zamenom, dok je vrednost mape mapa reči od kojih se dobija taj isti ključ. 
– KaMP 2 i KaMP 2.1 ispisuju one parove ekscerpiranih reči čiji se članovi (I) razlikuju kada se prebace u mala slova (II) podudaraju po rečima sa zamenom, dakle parove reči koje se razlikuju samo po zadatim podniskama. 
Na primer, ako su zadate podniske "a" i "u", a u ulaznoj datoteci je samo niska "Knjiga, knjigu, sveska, SVESKU", KaMP 2 i KaMP 2.1 neće ispisati niske "Knjiga ~ SVESKU" i "knjigu ~ sveska", pošto niska "knjig" nije jednaka nisci "svesk", nego će ispisati niske "Knjiga ~ knjigu" i "sveska ~ SVESKU". 
KaMP 2 i KaMP 2.1 imaju (A) mod u kom ignorišu razlike između velikih i malih slova ali favorizuju niske malih slova i (B) mod u kom bi npr. ekscerpirane niske "vitraž" i "Vitraž" obradili kao zasebne reči (v. Tabelu 7). 
Razlog je sledeći opravdani komentar iz (Aleksić and Šandrih 2021, 574): „Postavlja se samo pitanje važnosti veličine slova.“ 
Na primer, u nastavi srpskog kao stranog jezika vlastite imenice nekad imaju prioritet nad nevlastitim rečima. 
Ime Čak (Beri, Noris...) pogodno je za vežbu izgovora sa fotografijama; kakva bi fotografija dočarala značenje nepromenljive reči čak? 
U modu B, KaMP 2 i KaMP 2.1 iz korpusa POL ispisuju ne samo par "čak ~ Žak" nego i par "Čak ~ Žak" (uz "Čak ~ ŽAK" itd.). 
Sadržaj ulazne                 datoteke                 "EUPRAVE, eUprave,                 euprave, EUPRAVA,                 eUprava, euprava"                 Niska za izlaz                 KaMP-a                 "EUPRAVA ~ EUPRAVE"                 (ili "EUPRAVE ~                 EUPRAVA")                 Niska za izlaz                 KaMP-a 2 i                 KaMP-a 2.1 u modu A                 "euprava ~ euprave"                 Niske za izlaz                 KaMP-a 2 i                 KaMP-a 2.1 u modu B                 "euprava ~ euprave",                 "euprava ~ eUprave",                 "euprava ~ EUPRAVE",                 "eUprava ~ euprave",                 "eUprava ~ eUprave",                 "eUprava ~ EUPRAVE",                 "EUPRAVA ~ euprave",                 "EUPRAVA ~ eUprave",                 "EUPRAVA ~ EUPRAVE" 
Tabela 7. KaMP i KaMP 2 / KaMP 2.1: (ne)razlikovanje velikih i malih slova (ako su zadate podniske "a" i "e") 
Funkcija segmentacija_korpusa() reorganizovana je 
Ona korpus više ne učitava pomoću beskonačne while-petlje, nego, po ugledu na primer preporučenog načina za pozivanje funkcije do stražarske vrednosti iz (Hettinger 2021, 12.27 i dalje), pomoću for-petlje, koja je „brza i lepa“. 
KaMP nađene parove sortira prema Unicode-kodnim pozicijama prostih slova, dok KaMP 2 i KaMP 2.1 nađene parove sortiraju po pozicijama prostih slova u niskama mali_alfabet i veliki_alfabet (v. Tabelu 8). 
1 # Niske za sortiranje u KaMP-u 2 i KaMP-u 2.1             2 mali_alfabet = "- ~ abcčćdđefghijklmnopqrsštuvwxyzž"             3 veliki_alfabet = "- ~ABCČĆDĐEFGHIJKLMNOPQRSŠTUVWXYZŽ" 
Ulazna lista [             "nota ~ note",             "đaka ~ đake",             "Bač ~ Beč"             ]             Ulazna lista             sortirana onako             kako KaMP sortira             parove             [             "Bač ~ Beč",             "nota ~ note",             "đaka ~ đake"             ]             Ulazna lista             sortirana onako             kako KaMP 2 i             KaMP 2.1 sortiraju             parove             [             "Bač ~ Beč",             "đaka ~ đake",             "nota ~ note"             ] 
Tabela 8. KaMP i KaMP 2 / KaMP 2.1: sortiranje parova 
Istina, i novi KaMP-ovi sortirajući parove koriste Unicode-kodne pozicije, ali samo kod karaktera kojih nema u niskama za sortiranje (v. Tabelu 9). 
Par Lista za             sortiranje             Poreklo             broja             α 945 Unicode             - 0 Niska             z 32 mali_alfabet             r 23             a 3             č 6             e 10             n 19             j 15             e 10             1             ~ 2             1             β 946 Unicode             - 0 Niska             z 32 mali_alfabet             r 23             a 3             č 6             e 10             n 19             j 15             e 10 
Tabela 9. KaMP 2 / KaMP 2.1: primer liste za sortiranje 
KaMP 2 i KaMP 2.1 sortiraju članove svakog para pre nego što ih spoje u nisku za izlaz (npr. ["knjigu", "Knjiga"] → ["Knjiga", "knjigu"]). 
Brzina je merena u Python-u 3.8.2, na Manjaro Linux-u, računarom sa procesorom i5-11600K i dva DDR4-3200 CL16 SDRAM-a od po 16 GB i na korpusu POL, koji „broji oko 117.900.900 reči iz 223.308 tekstova sa sajta Politika“ (Aleksić and Šandrih 2021, 575). 
Funkcija koja pronalazi parove u KaMP-u 2 zasnovana je na Dekartovom proizvodu dve liste. 
Ovaj način predstavlja elegantno rešenje u smislu preglednosti i kompleksnosti koda, ali zbog kvadratnog ponašanja nije dovoljno efikasan u slučaju listi sa velikim brojem elemenata. 
Problem se lako uočava iz eksperimentalnih rezultata, gde se primećuje značajno duže vreme izvršavanja u slučaju podniski koje se češće pojavljuju (ma-va) (up. Tabelu 10). 
Brzina u sekundama             (prosek pet             sukcesivnih merenja)             KaMP             KaMP 2 KaMP 2.1             Mod A             Mod B             Mod A             Mod B             "č",             "d"             334 199 246 67 66             "dž",             "d"             135 81 85             "nadnad",             "supersuper"             6639 65,79 65,51 66,38 66,09             "ir",             "zir"             143 86 90 67 (!) 66             "ma",             "va"             2153 1257 1516 
Tabela 10. KaMP, KaMP 2 i KaMP 2.1: brzina izvršavanja 
U realnim uslovima, koji mogu zahtevati da se ovaj program pokreće na slabijim računarima, samo učitavanje korpusa i izdvajanje reči koje sadrže tražene podniske može da traje previše dugo. 
Na primer, učitavanje korpusa POL.xml na jednom starijem laptop računaru (Acer Aspire 3, Intel Quad Core N3710, 4GB RAM) traje i do približno 15 minuta. 
Predlog je da se doda i opcija kreiranja rečnika od korpusa koji bi se sačuvao na disku. 
Ova obrada korpusa bi se izvršila samo jednom, a kasnije bi se rečnik koristio za pronalaženje parova za nove podniske. 
Sledeći mogući korak u skraćivanju vremena izvršavanja jeste paralelizacija pretrage. 
Danas i slabiji računari imaju više jezgara“ u okviru procesora (na primer, računar iz prethodnog pasusa ima 4 jezgra). 
Zato je moguće neke delove koda paralelno izvršavati i time dodatno ubrzati program. 
Jedan predlog za jednostavnu paralelizaciju jeste podela jedne od listi reči na n delova, a zatim obrada tih delova na različitim procesorima (jezgrima) paralelno. 
Kako je i sekvencijalna verzija sa heširanjem već veoma efikasna, prvo treba rešiti problem sa sporim učitavanjem korpusa, pa tek onda razmišljati o daljim ubrzanjima. 
U poređenju sa KaMP-om, KaMP 2 i KaMP 2.1 za kraće vreme postižu više — nalaze praktično iste parove i nađene parove i reči unutar nađenih parova još sortiraju po boljem metodu. 
KaMP 2.1 je u većini ispitanih slučajeva bio nesporno brži od KaMP-a 2. 
1 """KaMP 2.1 je modifikovana verzija KaMP-a 2. KaMP 2 je             2 pak modifikovana verzija KaMP-a.             3 Uparivanje u funkcijama KaMP_2_1_a() i KaMP_2_1_b()             4 doprinos je L. Mrkele, a ostatak koda (sa funkcijama             5 ekscerp(), obrada_reči_1() i obrada_reči_2()) doprinos je             6 D. Aleksića.             7 """             8             9             10 def main ():             11 from functools import partial             12 from itertools import product             13 import re             14 import sys             15             16 sys . stdout . reconfigure ( encoding ="utf -8")             17 """V. (Aleksić and Šandrih 2021, 580)."""             18 prvo_slovo = "ma". casefold ()             19 drugo_slovo = "va". casefold ()             20 preklapanje = False             21 razl_vel_i_mal_slova = False             22 sort_znak = chr (1114111)             23 mali_alfabet = "- abcč´cddefghijklmnopqrsˇstuvwxyzž"             24 veliki_alfabet = "- ABCč´CDDEFGHIJKLMNOPQRSˇSTUVWXYZˇZ"             25             26 def spajanje_niski (* niske ):             27 return "". join ( niske )             28             29 def segmentacija_korpusa (             30 korpus , veli čina =8192 , separator ="\n"):             31 """Up. (581). 
32 Funkcija vraća delove korpusa zadate             33 veličine po zadatom separatoru.             34 """             35 ostatak = ""             36 for komad in iter (             37 partial ( korpus .read , veli č ina), ""):             38 """V. (Hettinger 2021, 12.27 i dalje)."""             39 komad = spajanje_niski ( ostatak , komad )             40 if separator in komad :             41 delovi = komad . rsplit ( separator , 1)             42 """V. (W3Schools 2021)."""             43 yield delovi [0]             44 ostatak = delovi [1]             45 else :             46 ostatak = komad             47 if ostatak :             48 yield ostatak             49             50 def prvo_mala_slova_1 (reč):             51 """Ključ za sortiranje reči koji favorizuje reči             52 sačinjene od malih slova.             53 """             54 if reč. islower ():             55 return "!"             56 elif reč. istitle ():             57 return sort_znak             58 else :             59 for slovo in reč:             60 if slovo . isupper ():             61 reč = reč. replace (slovo , sort_znak )             62 return reč             63             64 def prvo_mala_slova_2 (reč):             65 """Ključ za sortiranje nađenih parova. 
66 Na vrhu liste će biti parovi koji sadrže manje velikih slova.             67 """             68 if reč. islower ():             69 return "!"             70 else :             71 reč = reč. replace (" ~ ", "")             72 if reč. istitle ():             73 return sort_znak             74 else :             75 for slovo in reč:             76 if slovo . isupper ():             77 reč = reč. replace (             78 slovo , sort_znak )             79 return reˇ             80             81 def indeksiranje_za_listu (reč):             82 """Up. (Stack Overflow 2021c).             83 Ključ za sortiranje po zadatom redosledu.             84 """             85 lista_za_sort = []             86 for slovo in reč:             87 if slovo in mali_alfabet :             88 lista_za_sort . append (             89 mali_alfabet . index ( slovo ))             90 elif slovo in veliki_alfabet :             91 lista_za_sort . append (             92 veliki_alfabet . index ( slovo ))             93 else :             94 lista_za_sort . append (ord( slovo ))             95 return lista_za_sort             96             97 def prosta_zamena_slova (reč):             98 """U rečima se zadate podniske zamenjuju             99 specijalnom niskom.             100 """             101 if prvo_slovo in reč and drugo_slovo not in reč:             102 reč _sa_zamenom = reč. replace (             103 prvo_slovo , "\ u23B2 ")             104 elif prvo_slovo not in reč and drugo_slovo in reč:             105 reč _sa_zamenom = reč. replace (             106 drugo_slovo , "\ u23B2 ")             107 elif prvo_slovo in reč and drugo_slovo in reč:             108 reč _sa_zamenom = reč. replace (             109 prvo_slovo , "\ u23B2 ")             110 reč _sa_zamenom = reč _sa_zamenom . replace (             111 drugo_slovo , "\ u23B2 ")             112 return (reč _sa_zamenom ,)             113             114 def slož ena_zamena_slova (reč, slovo ): 
115 """U rečima se zadate podniske zamenjuju             116 specijalnom niskom.             117 Pokrivaju se slučajevi kada između             118 zadatih podniski ima preklapanja.             119 V. (Aleksić and Šandrih 2021, 580–-81).             120 """             121 izlazni_skup = set ()             122 reč _za_obradu = reč. replace (slovo , "{}")             123 for kombinacija in product (             124 [slovo , "\ u23B2 "],             125 repeat =reč _za_obradu . count ("{}")):             126 reč _sa_zamenama = reč _za_obradu . format (             127 * kombinacija )             128 if reč _sa_zamenama != reč:             129 izlazni_skup .add(             130 reč _sa_zamenama )             131 return izlazni_skup             132             133 def zamena_slova (reč):             134 if not preklapanje :             135 return prosta_zamena_slova (reč)             136 else :             137 skup = set ()             138 skup . update (             139 slo ž ena_zamena_slova (reč, prvo_slovo ),             140 slo ž ena_zamena_slova (reč, drugo_slovo ))             141 return skup             142             143 def tokenizacija ():             144 """Korpus se pretvara u rečnik reči izdvojenih pomoću             145 regularnog izraza. Izbegnuta je upotreba vrlo složenih             146 regularnih izraza u slučajevima kada su zadate             147 podniske duže (v. Tabelu 10).             148 Up. Odeljak 6. 
149 """             150 rečnik = {}             151 with open (r" /.../ POL. xml",             152 "r", encoding ="utf -8") as korpus :             153 komadi = segmentacija_korpusa ( korpus )             154 for komad in komadi :             155 pogoci = re. findall (             156 "[A-Za-z´C-ž-\ u00ad ]+", komad )             157 """V. (573–-74)."""             158 for pogodak in pogoci :             159 reč = pogodak . strip ("-")             160 if "\ u00ad " in reč:             161 reč = reč. replace ("\ u00ad ", "")             162 """V. (581)."""             163 rečnik[reč] = reč. casefold ()             164 return rečnik             166 def ekscerp ( slovo ):             167 """Iz rečnika dobijenog od korpusa uzimaju se reči             168 koje sadrže zadatu podnisku.             169 """             170 return ( klju č             171 for klju č, vrednost             172 in reč nik_od_korpusa . items ()             173 if slovo in vrednost )             174             175 def dekart ( lista_1 , lista_2 ):             176 """Eliminišu se neželjeni parovi iz Dekartovog             177 proizvoda obrađenih reči.             178 """             179 return (             180 (* sorted ([b, e]) , a, d)             181 for (a, b, c), (d, e, f)             182 in filter (             183 lambda torka : torka [0][2] == torka [1][2]             184 and torka [0][1] != torka [1][1] ,             185 product ( lista_1 , lista_2 , repeat =1)))             186             187 def obrada_re či_1(gen):             188 """Vraćaju se torke u kojima su reči, reči prebačene             189 u mala slova i reči sa zamenama.             190 Ova funkcija se poziva kada se želi zanemariti             191 razlika između velikog i malog slova. 
192 """             193 lista_torki = []             194 broja č = set ()             195 lista_re či = sorted (             196 list (gen), key= prvo_mala_slova_1 )             197 for reč in lista_re či:             198 reč _malim_slovima = reč nik_od_korpusa [reč]             199 if reč _malim_slovima not in broja č:             200 broja č.add (reč _malim_slovima )             201 for reč _sa_zamenom in zamena_slova (             202 reč _malim_slovima ):             203 lista_torki . append (             204 (reč, reč _malim_slovima ,             205 reč _sa_zamenom ))             206 return lista_torki             207             208 def obrada_re či_2(gen):             209 """Vraćaju se torke u kojima su reči, reči prebačene             210 u mala slova i reči sa zamenama.             211 Ova funkcija se poziva kada se NE ŽELI zanemariti             212 razlika između velikog i malog slova. 
213 """             214 lista_torki = []             215 for reč in gen:             216 reč _malim_slovima = reč nik_od_korpusa [reč]             217 for reč _sa_zamenom in zamena_slova (             218 reč _malim_slovima ):             219 lista_torki . append (             220 (reč, reč _malim_slovima ,             221 reč _sa_zamenom ))             222 return lista_torki             223             224 def KaMP_2_a ():             225 """Završna obrada u KaMP-u 2 ako se želi zanemariti             226 razlika između velikog i malog slova.             227 """             228 broja č = set ()             229 for torka in dekart (             230 obrada_re či_1( ekscerp (             231 prvo_slovo )),             232 obrada_re či_1( ekscerp (             233 drugo_slovo ))):             234 if ( torka [0] , torka [1]) not in broja č:             235 broja č.add (( torka [0] , torka [1]) )             236 kona č ni_skup .add (( torka [2] , torka [3]) )             237 lista_parova = [             238 " ~ ". join ( sorted ( list ( torka ),             239 key = indeksiranje_za_listu ))             240 for torka in kona č ni_skup ]             241 lista_parova . sort (key = indeksiranje_za_listu )             242 for par in lista_parova :             243 print (par )             244 print ("\n\ tBROJ PAROVA :")             245 print ("\t\t", len( lista_parova ))             246             247 def KaMP_2_b ():             248 """Završna obrada u KaMP-u 2 ako se NE ŽELI zanemariti             249 razlika između velikog i malog slova.             250 """             251 kona č ni_skup = {( torka [2] , torka [3])             252 for torka in dekart (             253 obrada_re či_2( ekscerp (             254 prvo_slovo )),             255 obrada_re či_2( ekscerp (             256 drugo_slovo )))}             257 lista_parova = [             258 " ~ ". join ( sorted ( list ( torka ),             259 key = indeksiranje_za_listu ))             260 for torka in kona č ni_skup ]             261 lista_parova = list ( set( lista_parova ))             262 lista_parova . sort (key = prvo_mala_slova_2 )             263 lista_parova . sort (key = indeksiranje_za_listu )             264 for par in lista_parova :             265 print (par )             266 print ("\n\ tBROJ PAROVA :")             267 print ("\t\t", len( lista_parova ))             268             269 def KaMP_2_1_a ():             270 """Uparivanje reči i završna obrada u KaMP-u 2.1             271 ako se želi zanemariti razlika između velikog             272 i malog slova.             273 """             274 lista1 = obrada_re č i_1( ekscerp (             275 prvo_slovo ))             276 lista2 = obrada_re č i_1( ekscerp (             277 drugo_slovo ))             278 mapa = {}             279 for x in lista2 :             280 if x[2] not in mapa :             281 mapa [x [2]] = {}             282 mapa [x [2]][ x [0]] = x [1]             283 for torka in lista1 :             284 result = mapa .get( torka [2])             285 if result is not None :             286 for k, v in result . items ():             287 if ( torka [1] != v and (k, torka [0])             288 not in kona č ni_skup ):             289 kona č ni_skup .add (( torka [0] , k))             290 lista = []             291 for par in kona č ni_skup :             292 par = list (par)             293 par . sort ( key= indeksiranje_za_listu )             294 izlaz = spajanje_niski (par [0] , " ~ ", par [1])             295 lista . append ( izlaz )             296 lista . sort (key= indeksiranje_za_listu )             297 for par in lista :             298 print (par )             299 print (" Broj parova : ", len( lista ))             300             301 def KaMP_2_1_b ():             302 """Uparivanje reči i završna obrada u KaMP-u 2.1             303 ako se NE ŽELI zanemariti razlika između velikog             304 i malog slova. 
305 """             306 lista1 = obrada_re č i_2( ekscerp (             307 prvo_slovo ))             308 lista2 = obrada_re č i_2( ekscerp (             309 drugo_slovo ))             310 mapa = {}             311 for x in lista2 :             312 if x[2] not in mapa :             313 mapa [x [2]] = {}             314 mapa [x [2]][ x [0]] = x [1]             315 for torka in lista1 :             316 result = mapa .get ( torka [2])             317 if result is not None :             318 for k, v in result . items ():             319 if ( torka [1] != v and (k, torka [0])             320 not in kona č ni_skup ):             321 kona č ni_skup .add (( torka [0] , k))             322 lista = []             323 for par in kona č ni_skup :             324 par = list (par)             325 par . sort ( key= indeksiranje_za_listu )             326 izlaz = spajanje_niski (par [0] , " ~ ", par [1])             327 lista . append ( izlaz )             328 lista . sort (key= prvo_mala_slova_2 )             329 lista . sort (key= indeksiranje_za_listu )             330 for par in lista :             331 print (par )             332 print (" Broj parova : ", len( lista ))             333             334 if ( prvo_slovo [ -1:] == drugo_slovo [:1]             335 or prvo_slovo [:1] == drugo_slovo [ -1:]             336 or ( prvo_slovo in drugo_slovo             337 or drugo_slovo in prvo_slovo )):             338 preklapanje = True             339 kona č ni_skup = set ()             340 reč nik_od_korpusa = tokenizacija ()             341 if razl_vel_i_mal_slova :             342 KaMP_2_1_b () # KaMP 2 poziva funkciju KaMP_2_b().             343 else :             344 KaMP_2_1_a () # KaMP 2 poziva funkciju KaMP_2_a().             345             346             347 if __name__ == " __main__ ":             348 main () 
1 """Pristup iz Minpair-a.             2 """             3 from collections import defaultdict             4             5 mapa_1 = defaultdict ( lambda : {})             6 for x in lista_2 :             7 mapa_1 [x [2]][ x [0]] = x[1]             8             9 """Pristup iz KaMP-a 2.1.             10 """             11 mapa_2 = {}             12 for x in lista_2 :             13 if x[2] not in mapa_2 :             14 mapa_2 [x [2]] = {}             15 mapa_2 [x [2]][ x [0]] = x[1]             16             17 """Ulaz je bila lista torki tipa ("subsidiaries",             18 "subsidiaries", "subsidi.ri.s"), napravljena od             19 reči sa "a" i/ili "e" iz cmudict-a.             20             21 Pristup iz KaMP-a 2.1 pokazao se oko 7% bržim             22 u Python-u 3.8.2 na sistemu opisanom u Odeljku 4.             23 Poređeni su proseci 500 sukcesivnih merenja             24 (55 ms : 51 ms).             25 """ 
1 """Pristup iz Minpair-a.             2 """             3 vowels_regex = re. compile (r’^(?:%s)’ % ’|’. join ( vowels ))             4 matches = [ vowels_regex . search ( phone ) for phone in word ]             5 list_with_repl = []             6 for i, character in enumerate ( word ):             7 for j, match in enumerate ( matches ):             8 if i == j:             9 if match :             10 list_with_repl . append (".")             11 else :             12 list_with_repl . append ( character )             13 string_with_repl = "". join ( list_with_repl )             14             15 """Pristup iz KaMP-a 2 i KaMP-a 2.1.             16 """             17 reč _sa_zamenom = reč. replace (             18 prvo_slovo , ".")             19 reč _sa_zamenom = reč _sa_zamenom . replace (             20 drugo_slovo , ".")             21             22 """Ulaz su bile reči sa "a" i/ili "e" iz             23 cmudict-a.             24             25 Pristup iz KaMP-a 2 i KaMP-a 2.1 pokazao se             26 oko 95% bržim u Python-u 3.8.2 na sistemu             27 opisanom u Odeljku 4. Poređeni su proseci             28 500 sukcesivnih merenja (472 ms : 23 ms).             29 Međutim, mora se istaći da u Minpair-u kˆod             30 za zamenjivanje dobija listu, a vraća torku             31 (npr. ["L", "UW", "S"] → ("L", ".", "S")),             32 dok je u KaMP-u 2 i KaMP-u 2.1 i ulaz i izlaz             33 koda za zamenjivanje –- niska             34 (npr. "teorijska" → "t orijsk ").             35 """ 
