Tak zatímco předchozí vlákno jsem úspěšně vyřešil, třídu pro self.transpose jsem vytvořil, přemýšlím nad přehledností a jednoduchostí původní třídy. Nejprve seznámíme publikum co bylo cílem kódu. Původní myšlenka byla: ze zkopírovaného textu s konjugačních tabulek získat koncovky ke kořenům. To se taky děje. Ale pak jsem zavedl třídu Transpose abych získal nové možnosti - metody.
Mám takovýto kód
if check_add_unique:
parser.print_unique_koncovky(sort)
else:
parser.transpose.print_column("Indicative", "Present", True)
Je-li check_add_unique True, pak získám přibližně podobný výpis:
explicar|explic|o|aba|aría|aré|as|aste|abas|arías|arás|a|ó|ará|amos|ábamos|aríamos|aremos|áis|asteis|abais|aríais|aréis|an|aron|aban|arían|arán|ara|ase|are|aras|ases|ares|áramos|ásemos|áremos|arais|aseis|areis|aran|asen|aren|ad|ando|ado|ar
explicar|expliqu|é|e|es|emos|éis|en
tento výpis vzniká velmi jednoduše během parsování txt tabulky po řádcích.
Přitom původní tabulky vypadají přibližně takto:
Indicative of "explicar"
Irregularities are in red
Present
Preterite
Imperfect
Conditional
Future
yo
explico
expliqué
explicaba
explicaría
explicaré
tú
explicas
explicaste
explicabas
explicarías
explicarás
él/ella/Ud.
explica
explicó
explicaba
explicaría
explicará
nosotros
explicamos
explicamos
explicábamos
explicaríamos
explicaremos
vosotros
explicáis
explicasteis
explicabais
explicaríais
explicaréis
ellos/ellas/Uds.
explican
explicaron
explicaban
explicarían
explicarán
Nejsložitější je metoda def parse_line(self, line) s tou není nutné vás seznamovat.
Nastavuje celou řadu vlastností na základě toho co zrovna na daném řádku naleznu. Je li to slovo označující název konjugační tabulky, nebo čas, nebo zájmeno, apod. tak z toho vyvodím určitý závěr a udržuji přehled pomocí vlastností jako self.column_counter ...
V metodě se nakonec volá metoda
self.add_form_to_kmen(line)
čímž se jednoduše přidá koncovka k danému seznamu.
Jelikož toto parsování bylo původně co nejjednodušší, a třídu Transpose se strukturovaným kontejnerem na data jsem vytvořil později, tato metoda a tato třída nemá strukturované ukládání dat.
Začíná takto:
def add_form_to_kmen(self, sword):
preceding_words = [] # Proměnná na uložení slov,
preceding_words zjišťuje jestli se před daným slovesem vyskytují nějaké jiné slova jako různé formy slovesa mít nebo být nebo zápor a to se ukládá sem. Takže z toho je vidět, že to opravdu není strukturované.
Dále tam mám takový blok if check_add_unique:, který se snaží podívat do pole self.verb_forms[kmen] abych zjistil jestli se koncovka neopakuje... abych prostě zmenšil počet prvků v poli. Tento přidávací blok vypadá takto:
self.verb_forms_check[kmen] += 1 # Počítám kolik koncovek jsem přidal
self.verb_forms[kmen].append(sword)
self.verb_forms_precedes[kmen].append(preceding_string) # Přidáme předcházející slova
return True
self.verb_forms_check[kmen] += 1 # Počítám kolik koncovek jsem přidal
self.verb_forms[kmen].append(koncovka)
Na a mě napadlo, že kdybych to chtěl dále rozšiřovat a mít metody, které vyfiltrují třeba jen určitou konjugační tabulku nebo jen řádky pro určitou osobu, tak to vlastně nejde, protože nepoužívám žádný strukturovaný kontejner jako v případě self.Transpose ... musel bych tedy tyto speciální parametry přidávat přímo do té metody add_form_to_kmen plus ještě vytvořit přídavné pole, které by shromažďovalo informace o osobě a názvu konjugační tabulky (filtrovat čas by možná ani nešlo - nebo šlo, ale složitě).
Tato třída VerbParser např. obsahuje tyto vlastnosti:
class VerbParser: def __init__(self, sloveso, hledane_kmeny):
self.verb_forms = {kmen: [] for kmen in hledane_kmeny}
self.verb_forms_precedes = {kmen: [] for kmen in hledane_kmeny} # Toto je kontejner na slovíčka, která nejsou součástí kmene zpracovávaného slovesa, nutno sbírat i prázdné výskyty.
self.verb_forms_check = {kmen: 0 for kmen in hledane_kmeny} # data o tom kolik je tam záznamů - !!! Také slovo bez koncovky se počítá do verb_forms_check jako jeden z tvarů
self.verb_no_form = {kmen: False for kmen in hledane_kmeny} # data o tom zda se daný kmen nachází na řádku v identické podobě bez koncovky
self.verb_forms_dupl_ignored = 0
z těch polí pak dokážu provést výpis.... Což je něco na tomto principu:
def print_unique_koncovky(self, sort):
"""Vypíše kmeny s nalezenými koncovkami tak aby se neopakovaly stejné koncovky na jednom řádku."""
if sort:
locale.setlocale(locale.LC_ALL, 'es_ES.utf8')
with open(output_file, "w") as file:
for kmen, koncovky in self.verb_forms.items():
line_out = f"{self.sloveso}|{kmen}|"
# Pro každou koncovku zkontroluj, jestli už není v line_out
if sort:
mkoncovky = sorted(koncovky, key=locale.strxfrm)
else:
mkoncovky = koncovky
for koncovka in mkoncovky:
# Kontrola, zda se již daná koncovka v řetězci nevyskytuje
if f"|{koncovka}|" not in line_out and not line_out.endswith(f"|{koncovka}"):
line_out += f"{koncovka}|"
Je mi jasné, že se používají dva odlišné způsoby práce s daty. Jeden je surové parsování, nasbíráni do pole a druhý je strukturovaný, kde je více možností. U tohoto nestrukturovaného to moc nejde. První věc asi by každého napadlo to předělat vytvořit kontejner se strukturovanými daty něco jako mám u Transpose:
# Pomocná třída, která umožňuje zjistit více informací o hotovém tvaru (stringu)
class SimpleVerbInfo:
def __init__(self, adverb, person):
self.adverb = adverb
self.person = person # 1-6
def __repr__(self):
return f"VerbInfo(adverb={self.adverb}, person={self.person})"
class VerbInfo:
def __init__(self, adverb, preceding, person):
self.adverb = adverb
self.preceding = preceding
self.person = person # 1-6
def __repr__(self):
return f"VerbInfo(adverb={self.adverb}, preceding={self.preceding}, person={self.person})"
class Transpose():
# Časy které neobsahují text před konjugací
self.simple_times = [ "Indicative", "Subjunctive" ]
# Definujeme relevantní časy pro každou konjugaci pro kontejner stringů
self.conjugations = {
"Indicative": {time: [] for time in ["Present", "Preterite", "Imperfect", "Conditional", "Future"]},
"Subjunctive": {time: [] for time in ["Present", "Imperfect", "Future"]},
"Imperative": {time: [] for time in ["Affirmative", "Negative"]},
"Progressive": {time: [] for time in ["Present", "Preterite", "Imperfect", "Conditional", "Future"]},
"Perfect": {time: [] for time in ["Present", "Preterite", "Imperfect", "Past", "Conditional", "Future"]},
"Perfect Subjunctive": {time: [] for time in ["Present", "Past", "Future"]},
"Informal Future": {"Informal Future": []} # Specifické jen pro Informal Future
}
# Definujeme předcházející řetězce jen pro relevantní informace ke stringu
self.container = {
"Indicative": {time: [] for time in ["Present", "Preterite", "Imperfect", "Conditional", "Future"]},
"Subjunctive": {time: [] for time in ["Present", "Imperfect", "Future"]},
"Imperative": {time: [] for time in ["Negative"]}, # Předcházející stringy jen u negativního imperativu
"Progressive": {time: [] for time in ["Present", "Preterite", "Imperfect", "Conditional", "Future"]},
"Perfect": {time: [] for time in ["Present", "Preterite", "Imperfect", "Past", "Conditional", "Future"]},
"Perfect Subjunctive": {time: [] for time in ["Present", "Past", "Future"]},
"Informal Future": {"Informal Future": []}
}
Ovšem tím by se zase zesložitil kód a bylo by to předělávání již funkčního kódu. První věc co mě napadla: izolovat parsování od metod pro výpis dat (tím že bych zavedl kontejner), ale zase se mi do toho nechce, protože původní myšlnenka byla přece jednoduchá, jen nasbírat ty koncovky jako jsem ukázal na začátku. Proč to tedy přeplácávat? Současně ale láká možnost použít další metodu, pomocí které jednoduše má člověk možnost vytisknout si jen tvar pro určité sloveso u dané osoby nebo např. jen pro Subjunktiv či jen pro Perfect.
Šly byste cestou přidávání polí jako
self.verb_forms
self.verb_forms_precedes = {kmen: [] for kmen in hledane_kmeny}
self.verb_forms_check = {kmen: 0 for kmen in hledane_kmeny}
self.verb_no_form = {kmen: False for kmen in hledane_kmeny}
nebo byste našly nějaké třetí řešení, medium difficulty approach?
Pro easy difficulty approach stačí pole typu:
{kmen: [] for kmen in hledane_kmeny}
ovšem pro každou koncovku (respektive pro každý neunikátní tvar) se musí sbírat název tabulky ... což znamená že se to tam bude opakovat třeba 30x.
Nebo třeba medium approach mít jen jedno přídavné pole s informacema, kde bude podobná třída jako VerbInfo,
Tedy asi takto?
class VerbInfo:
def __init__(self, adverb, preceding, person):
self.adverb = adverb
self.preceding = preceding
self.conjugation = conjugation
self.no_form = no_form
self.person = person # 1-6
self.check = check
def __repr__(self):
return f"VerbInfo(adverb={self.adverb}, conjugation={self.conjugation}, preceding={self.preceding}, no_form={self.no_form}, person={self.person}, check={self.check})"