Tak więc, starając się napisać program do koniugacji czasowników (algorytmicznie, nie poprzez zbiór danych) dla języka francuskiego, natknąłem się na niewielki problem.
Algorytm koniugacji czasowników jest właściwie dość prosty w przypadku 17 lub więcej przypadków czasowników i działa według określonego wzorca dla każdego przypadku; dlatego sufiksy koniugacji dla tych 17 klas są statyczne i (najprawdopodobniej) nie zmienią się w najbliższym czasie. Na przykład:
// Verbs #1 : (model: "chanter")
terminations = {
ind_imp: ["ais", "ais", "ait", "ions", "iez", "aient"],
ind_pre: ["e", "es", "e", "ons", "ez", "ent"],
ind_fut: ["erai", "eras", "era", "erons", "erez", "eront"],
participle: ["é", "ant"]
};
Są to fleksyjne przyrostki dla najczęstszej klasy czasowników w języku francuskim.
Istnieją inne klasy czasowników (nieregularności), których koniugacje najprawdopodobniej pozostaną statyczne przez następne stulecie lub dwa. Ponieważ są one nieregularne, ich całkowite koniugacje muszą zostać uwzględnione statycznie, ponieważ nie można w sposób niezawodny koniugować ze wzoru (istnieją również [według moich obliczeń] 32 nieregularności). Na przykład:
// "être":
forms = {
ind_imp: ["étais", "étais", "était", "étions", "étiez", "étaient"],
ind_pre: ["suis", "es", "est", "sommes", "êtes", "sont"],
ind_fut: ["serai", "seras", "sera", "serons", "serez", "seront"],
participle: ["été", "étant"]
};
Mógłbym umieścić to wszystko w XML lub nawet JSON i przekształcić z postaci szeregowej, kiedy trzeba go użyć, ale czy jest sens? Ciągi te są częścią języka naturalnego, który się zmienia, ale w wolnym tempie.
Obawiam się, że robiąc rzeczy „we właściwy sposób” i deserializując niektóre źródła danych, nie tylko skomplikowałem problem, który nie musi być skomplikowany, ale całkowicie cofnąłem się pod kątem całego celu podejście algorytmiczne: nie używać źródła danych! W języku C # mógłbym po prostu stworzyć klasę pod namespace Verb.Conjugation
(np. class Irregular
), Aby przechowywać te ciągi w wyliczonym typie lub coś, zamiast upychać je w XML i tworzyć class IrregularVerbDeserializer
.
Pytanie brzmi : czy właściwe jest stosowanie ciągów kodu, których zmiana jest bardzo mało prawdopodobna w trakcie życia aplikacji? Oczywiście nie mogę zagwarantować 100%, że się nie zmienią, ale ryzyko w porównaniu do kosztu jest prawie trywialne w moich oczach - kodowanie na stałe jest tutaj lepszym pomysłem.
Edycja : proponowany duplikat pyta, jak przechowywać dużą liczbę ciągów statycznych , a moje pytanie brzmi, kiedy powinienem na stałe zakodować te ciągi statyczne .
Odpowiedzi:
Wydaje mi się, że odpowiedziałeś na własne pytanie.
Jednym z największych wyzwań, przed którymi stoimy, jest oddzielenie rzeczy, które mogą się zmienić od rzeczy, które się nie zmienią. Niektórzy ludzie wariują i zrzucają absolutnie wszystko, co mogą, do pliku konfiguracyjnego. Inni przechodzą na drugą skrajność i wymagają rekompilacji nawet w przypadku najbardziej oczywistych zmian.
Wybrałbym najprostsze podejście do wdrożenia, dopóki nie znalazłem przekonującego powodu, aby uczynić go bardziej skomplikowanym.
źródło
French.Verb.Irregular.Etre
które zawierałyby dane z mojego pytania. Myślę, że wszystko działa dobrze;)if (num == 0xFFD8)
). Ten przykład powinien stać się podobnyif (num == JPEG_MAGIC_NUMBER)
do niemal wszystkich przypadków ze względu na czytelność. Po prostu zwracam na to uwagę, ponieważ słowo „twarde kodowanie” często podnosi włosy na szyjach ludzi (takich jak moje) z powodu tego alternatywnego znaczenia tego słowa.JPEG_START_OF_IMAGE_MARKER
?Rozumujesz w złym zakresie.
Nie zakodowałeś tylko pojedynczych czasowników. Zakodowałeś język i jego zasady . To z kolei oznacza, że aplikacji nie można używać w żadnym innym języku i nie można jej rozszerzać o inne reguły.
Jeśli taki jest twój zamiar (tj. Użycie go tylko w języku francuskim), jest to właściwe podejście, ze względu na YAGNI. Ale przyznajesz się, że chcesz użyć go później również w innych językach, co oznacza, że już wkrótce będziesz musiał przenieść całą zakodowaną część do plików konfiguracyjnych. Pozostałe pytanie to:
Czy, z pewnością blisko 100%, w najbliższej przyszłości rozszerzysz aplikację na inne języki? Jeśli tak, powinieneś teraz eksportować rzeczy do plików JSON lub XML (dla słów, części słów itp.) I dynamicznych języków (dla reguł) zamiast zmuszać się do przepisania większej części swojej aplikacji.
Czy jest tylko niewielkie prawdopodobieństwo, że aplikacja zostanie gdzieś rozszerzona w przyszłości, w takim przypadku YAGNI dyktuje, że najprostsze podejście (to, z którego teraz korzystasz) jest lepsze?
Jako przykład weźmy sprawdzanie pisowni Microsoft Word. Jak myślisz, ile rzeczy jest zakodowanych na stałe?
Jeśli tworzysz procesor tekstu, można zacząć od prostego silnika ortograficzny sztywno reguł i nawet Hardcoded słowami:
if word == "musik": suggestSpelling("music");
. Szybko zaczniesz przesuwać słowa, a potem będziesz rządzić się poza swoim kodem. Inaczej:Jak sam się podkreśliłeś:
Jak tylko kodujesz reguły dla każdego języka, każdy inny będzie wymagał coraz większej ilości kodu, szczególnie biorąc pod uwagę złożoność języków naturalnych.
Innym tematem jest sposób wyrażania tych różnych zasad, jeśli nie za pomocą kodu. Ostatecznie może się okazać, że język programowania jest najlepszym narzędziem do tego. W takim przypadku, jeśli trzeba rozszerzyć silnik bez jego ponownej kompilacji, dynamiczne języki mogą być dobrą alternatywą.
źródło
LanguageProcessor
klasę z wieloma podklasami. (W rzeczywistości „plik konfiguracyjny” jest w rzeczywistości klasą)Ciągi powinny zostać wyodrębnione do pliku konfiguracyjnego lub bazy danych, gdy wartości mogą ulec zmianie niezależnie od logiki programu.
Na przykład:
Wyodrębnianie tekstów interfejsu użytkownika do plików zasobów. Pozwala to nie-programistom edytować i sprawdzać teksty oraz umożliwia dodawanie nowych języków poprzez dodawanie nowych zlokalizowanych plików zasobów.
Wyodrębnianie parametrów połączenia, adresów URL do usług zewnętrznych itp. Do plików konfiguracyjnych. Pozwala to na użycie różnych konfiguracji w różnych środowiskach oraz na zmianę konfiguracji w locie, ponieważ mogą one wymagać zmiany z przyczyn niezależnych od aplikacji.
Sprawdzanie pisowni ze słownikiem słów do sprawdzenia. Możesz dodawać nowe słowa i języki bez modyfikowania logiki programu.
Ale jest też narzut związany z rozpakowywaniem do konfiguracji i nie zawsze ma to sens.
Ciągi mogą być zakodowane na stałe, gdy rzeczywisty ciąg nie może się zmienić bez zmiany logiki programu.
Przykłady:
W twoim przypadku myślę, że jasne jest, że słowa są integralną częścią logiki programu (ponieważ budujesz koniugator z określonymi regułami dla określonych słów), a wyodrębnianie tych słów do zewnętrznego pliku nie ma żadnej wartości.
Jeśli dodasz nowy język, i tak będziesz musiał dodać nowy kod, ponieważ każdy język ma określoną logikę koniugacji.
Niektórzy sugerują, że można dodać silnik reguł, który pozwala określić reguły koniugacji dla dowolnych języków, aby nowe języki można było dodawać wyłącznie przez konfigurację. Zastanów się bardzo, zanim pójdziesz tą drogą, ponieważ ludzkie języki są cudownie dziwne, więc potrzebujesz bardzo wyrazistego silnika reguł. Zasadniczo wymyśliłbyś nowy język programowania (koniugacja DSL) dla wątpliwych korzyści. Ale masz już do dyspozycji język programowania, który może zrobić wszystko, czego potrzebujesz. W każdym razie YAGNI.
źródło
Zgadzam się w 100% z odpowiedzią Dana Pichelmana, ale chciałbym dodać jedną rzecz. Pytanie, które powinieneś sobie zadać, brzmi: „kto będzie utrzymywać / rozszerzać / poprawiać listę słów?”. Jeśli zawsze jest to osoba, która również przestrzega zasad określonego języka (chyba konkretnego programisty), to nie ma sensu korzystać z zewnętrznego pliku konfiguracyjnego, jeśli to komplikuje sprawę - nie uzyskasz żadnych korzyści z to. Z tego punktu widzenia sensowne jest kodowanie takich list słów, nawet jeśli trzeba je od czasu do czasu zmieniać, o ile wystarczy dostarczyć nową listę jako część nowej wersji.
(Z drugiej strony, jeśli istnieje niewielka szansa, że ktoś inny będzie w stanie utrzymać listę w przyszłości lub jeśli chcesz zmienić listy słów bez wdrażania nowej wersji aplikacji, użyj osobnego pliku).
źródło
Nawet podczas hardcoding wydaje się dobrze tutaj, i lepiej niż dynamiczne ładowanie plików konfiguracyjnych, nadal polecam, że nie ściśle oddzielić swoje dane (słownik czasowników) od algorytmu . Możesz je skompilować bezpośrednio w aplikacji w procesie kompilacji.
Pozwoli ci to zaoszczędzić sporo mgiełki dzięki utrzymywaniu listy. W swoim VCS możesz łatwo stwierdzić, czy zatwierdzenie zmieniło algorytm, czy po prostu naprawić błąd koniugacji. Ponadto lista może wymagać dołączenia w przyszłości w przypadkach, które nie zostały wzięte pod uwagę. Zwłaszcza liczba 32 czasowników nieregularnych, które policzyłeś, nie wydaje się dokładna. Chociaż wydają się one obejmować powszechnie stosowane, znalazłem odniesienia do 133, a nawet 350 z nich.
źródło
Ważną częścią jest rozdzielenie obaw. Jak to osiągnąć, jest mniej istotne. tzn. Java jest w porządku.
Niezależnie od tego, w jaki sposób wyrażane są reguły, należy dodać język zmiany reguły: ile kodu i plików trzeba edytować?
Idealnie byłoby, gdyby dodanie nowego języka było możliwe poprzez dodanie pliku „english.xml” lub nowego „EnglishRules implementuje obiekt ILanguageRules”. Plik tekstowy (JSON / XML) daje przewagę, jeśli chcesz go zmienić poza cyklem życia kompilacji, ale wymaga złożonej gramatyki, analizy i trudniej będzie go debugować. Plik kodu (Java) pozwala wyrazić złożone reguły w prostszy sposób, ale wymaga przebudowy.
Zaczynam od prostego API Java za czystym językiem agnostycznym interfejsem - ponieważ jest to potrzebne w obu przypadkach. Zawsze możesz dodać implementację tego interfejsu zabezpieczoną plikiem XML później, jeśli chcesz, ale nie widzę potrzeby rozwiązania tego problemu od razu (ani nigdy).
źródło