Лого на страниците (малко). Система Orphus
Ако забележите грешка, маркирайте израза с мишката и натиснете Control+Enter. Благодаря!
Промени в програмното осигуряване
 
Програмистите обичат да пишат програми, ама никак не обичат да пишат обяснения към тях. То и аз не обичам, ама трябва.
Защото ние с вас програмисти не сме и сигурно няма да станем. Ние просто трябва да се научим да си обработваме текстовете с програми. Затова аз тук подробно ще обяснявам какво къде съм променил, пък вие експериментирайте със старите и с новите варианти, пък може да предложите и по-добър вариант. Тъй де — взаимоучителната система е най-добрата.
 
24 февруари 2017
21 май 2016
26 февруари 2015
 
24 февруари 2017
В gtools.vim не работеше вмъкването на маркери за диалог, монолог и пр. Аз нещо съм объркал, съжалявам.
При многоредово маркиране, например в списък, курсорът не застава в края на маркирания текст. Бял кахър, ама трябва да се отстрани.
 
21 май 2016
Каквото имаше да става, стана — в по-новите сглобки на линукс (например, в Ubuntu 16.04) vim/gvim е компилиран с поддръжка на Python 3.x, вместо с поддръжка на Python 2.x. А това означава, че модулът glatcyr.vim дава грешка още в реда
python << ENDPY
Тъй като различията между двете версии на питона са най-големи точно по отношение на стринговите памети, наложи се да преработя и целия питонски текст. Всъщност — да го съкратя. Така се появи и трети вариант на модула: glatcyr_py3.vim. Така сега има
glatcyr_win.vim — за Windows;
glatcyr.vim — за линукс с Python 2.x;
glatcyr_py3.vim — за линукс с Python 3.x.
Малки поправки има и в gtools.vim — включил съм коментар, разясняващ как да извикате със :source съответния модул с функцията LatCyr(). Променен е, разбира се, и общият архив gtools.zip и контролните суми.
 
26 февруари 2015
Повече или по-малко поправки във всички програми — предимно в информационните текстове.
 
gstat.py
Модулът gstat.py също е снабден с информационни текстове, надявам се да помагат.
Методът histo() е преименуван на bar_chart(), което по-вярно отразява нещата, а методът fishers_F() е преименуван на f_test().
Има и съществени промени в метода chi_square() — вместо да изнася теоретичните честоти, както прави методът chi2_contingency() от scipy.stats, методът chi_square() изнася частните хи-квадрат и сумите им по редове и колони. Това е значително по-интересно и значително по-информативно. Ама за теста хи-квадрат аз мисля да напиша и отделен текст с примери.
Тъй като методът chi_square() изнася доста информация в тип dictionary, създал съм и нов метод print_chi_square(), който печата на конзолата данните от chi_square() в четлив вид. В документния стринг към print_chi_square() има и пример.
 
gtools.py
В класа TreeSortDict() във вътрешния (служебния) метода _get_listdict_as_str(self, dictlist) е включен методът .rstrip(). Служебният метод _get_listdict_as_str(self, dictlist) е написан съвсем по „питонски“, тоест, доста нечетливо, целият е на един ред, ама тази добавка премахва една „грешка“ в работата на програмите за направа на речници (glist2dict.py и gtext2dict.py). По подразбиране, тоест, когато не инструктирате програмите да създават в речника поле с адресна информация, те поставяха в края на реда табулатор, тоест, оставяха полето за адреси „празно“. Не виждам в това смисъл и съм го премахнал.
В същия клас TreeSortDict() съществено съм преработил метода list2dict(self). Той изглеждаше така:
    def list2dict(self):
        """
        """
        addr = 0
        in_tree = self._put_in_tree
        punct = get_punctuation()

        for list_line in self._text.split('\n'):
            buf = list_line.split('\t')
            if len(buf) > 1:
                w = buf[0].strip(punct)
                c = buf[1]
            else:
                w = buf[0].strip(punct)
                c = ""
            if w != '':
                if self._a_tergo:
                    w = w[::-1]
                if self._lower:
                    w = w.lower()
                if self._addresses:
                    addr += 1
                    dn = DictNode(w, c, 1, str(addr))
                else:
                    dn = DictNode(w, '', 1, '')
                in_tree(dn)
Бях го замислил да може да обработва списъчно представен текст с класификатори, но не бях помислил за възможността да се поставят и честоти (вижте примера към ggrablist.py). Това е поправено и сега методът изглежда така:
    def list2dict(self):
        """
        """
        addr = 0
        in_tree = self._put_in_tree
        punct = get_punctuation()

        for list_line in self._text.split('\n'):
            buf = list_line.split('\t')
            if len(buf) == 1:
                w = buf[0].strip(punct)
                c = ''
                f = 1
            elif len(buf) == 2:
                w = buf[0].strip(punct)
                c = buf[1]
                f = 1
            elif len(buf) == 3:
                w = buf[0].strip(punct)
                c = buf[1]
                f = int(buf[2])
            if w != '':
                if self._a_tergo:
                    w = w[::-1]
                if self._lower:
                    w = w.lower()
                if self._addresses:
                    addr += 1
                    dn = DictNode(w, c, f, str(addr))
                else:
                    dn = DictNode(w, c, f, '')
                in_tree(dn)
В gtools.py има съществена промяна и в класа KphnBG(), която засяга, разбира се, и работата на програмата gkphn_bg.py. Ще разясня тези промени малко по-подробно.
Стремял съм се да напиша класа KphnBG() възможно най-просто. При инициализацията се подава стринг, тоест поредица от символи, които се обработват последователно, а резултатите се вписват в списък (list). Всъщност, на обработка се подлагат само кирилските букви – всички останали символи (букви, които не влизат в българската азбука, пунктуационни знакове, цифри, управляващи символи и пр.) просто се преписват в списъка self._textkphn.
Този прост подход е много ефективен: като стринг може да подадете обикновен (добре редактиран) текст или списъчно представен текст с класификатори и честоти. В този втори случай беше необходимо да се спазва само едно елементарно правило — да не се употребяват в класификаторите букви от българската азбука, за да не бъдат „преброени“ и те. Иначе преброяването на килофоните си върви добре.
И все пак в този прост подход има „дупка“, недомислие. Да си представим, че пишете клас, който да преброява килофоните в писменост, където използват латинската азбука. Няма да може да минете с този подход — как ще различавате символите от заглавката на списъчно представен текст от символите в класификатора?
Аз гледам на програмките в glotta не просто като на работни инструменти, а и като на учебен материал — може да видите как се прави това или онова, може да редактирате текстовете на програмките, да експериментирате. Затова запълних тази „дупка“ в работата на KphnBG().
Решението е просто — когато във входния поток се появи символ за табулатор, всички следващи символи трябва да се пренасочват директно към изходния списък, чак докато се появи символ за край на реда. Това е реализирано сега в метода self._getchar(). Малко е усложнен, ама не много. Ето как изглеждаше той преди:
    def _getchar(self):
        """ вземи поредния символ от текста """
        try:
            self._look = next(self._chargen)
        except StopIteration:
            self._look = ""
И ето как стана:
    def _getchar(self):
        """ вземи поредния символ от текста """
        try:
            self._look = next(self._chargen)
            if self._look == '\t':
                try:
                    while self._look != '\n':
                        self._emit(self._look)
                        self._look = next(self._chargen)
                except StopIteration:
                    self._look = ""
        except StopIteration:
            self._look = ""
Така че сега може да вписвате в класификаторите каквито си щете символи. Ама аз ви съветвам да продължите да се придържате към латиницата (в английския или латинския й вариант без диакритични знакове).
При такива дори уж малки и очевидни промени винаги могат да се получат изненади. Така стана и тук в един случай при обработката на д. Не във всички случаи, само в един. Ама това са и от най-трудните за откриване грешки, щото може да не съобразите, когато си преработвате програмния текст, а и може да не откриете грешката дълго време след това.
При разработката на KphnBG() аз съм приел, че дилитеремата дж винаги ще се обработва като монофона. Дилитеремата дз винаги ще се обработва като дифона. Това ми решение е добре обмислено и от филологическа, и от статистическа гледна точка, ама разясненията ще ги оставя за по-нататък.
Сега, от програмна гледна точка, проблемът е, че като се срещне литера д, не може да се вземе решение как да бъде тя обработена, докато не се „погледне“ следващата литера: ако тя е ж, преброява се една монофона за двете литери; ако е друга литера, д се отброява като монофона и се продължава по стандартния начин. Ето как изглеждаше методът self._make_d() преди редакцията:
    def _make_d(self, char):
        """ анализ на Д – има ли след него Ж? """
        self._getchar()
        if self._look in ['ж', 'Ж']:
            self._emit(char)
            self._emit_and_count(self._look)
            self._getchar()
        else:
            self._emit_and_count(char)
        self._make_after_consonant(self._look)
И сега да си представим какво става, ако заглавката завършва на д, например
пред P
Очевидно четенето „на късо“ в метода self._getchar() ще запише табулатора и класификатора преди д:
пре Pд
А не точно това искаме, нали?
Наложи се да попремисля малко логиката на метода self._make_d(). Защо трябва да правя изчитане напред, преди да взема решение? Няма ли да е по-разумно, ако запиша д/Д и преброя монофона веднага, а да обработвам ж/Ж само ако дойде непосредствено след д, без да я броя като монофона? В резултат на това не само реших проблема, но и съкратих малко метода self._make_d():
    def _make_d(self, char):
        """ анализ на Д – има ли след него Ж? """
        self._emit_and_count(char)
        self._getchar()
        if self._look in ['ж', 'Ж']:
            self._emit(self._look)
            self._getchar()
        self._make_after_consonant(self._look)
Само да подскажа — ако методът self._getchar() прави „прескачане“, крайният резултат е символът за край на реда и по никакъв начин не може да бъде ж, дори ако следващият ред започва с ж/Ж. Това разяснение — само ако имате някакви съмнения. Аз, например, имах.
Винаги е полезно да се помисли пак върху логиката на програмния текст — той често се опростява. А и на вас ви става по-ясно какво правите всъщност.