Jest to głównie teoretyczne pytanie o FP, ale wezmę udział w tekstowych przygodach (takich jak oldschoolowy Zork), aby zilustrować mój punkt widzenia. Chciałbym poznać twoje opinie na temat tego, jak modelowałbyś stanową symulację z FP.
Wydaje się, że przygody tekstowe wymagają OOP. Na przykład wszystkie „pokoje” są instancjami Room
klasy, możesz mieć Item
klasę podstawową i interfejsy, takie jak Item<Pickable>
rzeczy, które możesz nosić i tak dalej.
Modelowanie świata w FP działa inaczej, szczególnie jeśli chcesz wymusić niezmienność w świecie, który musi mutować w miarę postępów w grze (obiekty są przemieszczane, wrogowie są pokonani, liczba punktów rośnie, gracz zmienia lokalizację). Wyobrażam sobie jeden duży obiekt, World
który ma to wszystko: jakie pokoje możesz eksplorować, w jaki sposób są połączone, co niesie gracz, jakie dźwignie zostały uruchomione.
Myślę, że czystym podejściem byłoby po prostu przekazanie tego dużego obiektu do dowolnej funkcji i zwrócenie go (ewentualnie zmodyfikowanego). Na przykład mam moveToRoom
funkcję, która pobiera World
i zwraca ją wraz ze World.player.location
zmianą w nowym pokoju World.rooms[new_room].visited = True
i tak dalej.
Nawet jeśli jest to bardziej „poprawny” sposób, wydaje się, że egzekwuje czystość ze względu na to. W zależności od języka programowania przekazywanie tego potencjalnie bardzo dużego World
obiektu tam iz powrotem może być kosztowne. Ponadto każda funkcja może wymagać dostępu do dowolnego World
obiektu. Na przykład, pokój może być dostępny lub nie w zależności od dźwigni uruchomionej w innym pokoju, ponieważ może być zalany, ale jeśli gracz nosi kamizelkę ratunkową, może i tak wejść do niej. Potwór może być agresywny lub nie, w zależności od tego, czy gracz zabił swojego kuzyna w innym pokoju. Oznacza to, że roomCanBeEntered
funkcja musi mieć dostęp World.player.invetory
i World.rooms
, describeMonster
musi mieć dostęp World.monsters
i tak dalej (w zasadzie, to musiprzekazać cały ładunek). Wydaje mi się, że to naprawdę wymaga globalnej zmiennej, nawet jeśli jest to dobry styl programowania, szczególnie w FP.
Jak rozwiązałbyś ten problem?
źródło
Odpowiedzi:
Pamiętaj, że języki funkcjonalne używają struktur danych i oddzielnych funkcji zamiast obiektów. Na przykład zamiast świata miałbyś zestaw pokoi i listę przedmiotów ekwipunku.
Idealnie byłoby również ograniczyć ilość danych, które przekazujesz funkcjom, do tego, ile faktycznie wymagają one tak dużo, jak to możliwe, zamiast przechodzić przez cały świat (powiedz, że wyodrębniasz jeden odpowiedni pokój ze swojego świata; oczywiście całkowicie zależne od siebie światy mogą być trudne oddzielny). Wynik zostałby ponownie włączony do światowej struktury danych, tworząc nowy stan. Nie można modelować stanu bez użycia stanu; jak mówisz, niektóre rzeczy z natury wymagają mutacji.
Większość praktycznych języków funkcjonalnych zapewnia sposób realizacji mutacji albo bezpośrednio (powiedzmy monada ST w Haskell lub transjenty w Clojure), albo wydajnie ją symuluj (często przez ponowne użycie niezmienionych części struktury danych (dobrym przykładem są tutaj domyślne struktury danych Clojure)) ). Tak czy inaczej, utrzymywana jest czystość.
Ponieważ ilość stanu, który należy zmutować, wydaje się ograniczona, nie martwiłbym się zbytnio problemami z wydajnością i stosuję (prawdopodobnie już zoptymalizowane) naiwne podejście funkcjonalne.
Inną opcją, którą widziałem, byłoby zwracanie tylko instrukcji zmiany części świata z twoich indywidualnych funkcji, a następnie aktualizowanie świata zgodnie z nimi. Opis postów na blogu jest dostępny na stronie http://prog21.dadgum.com/23.html .
Obie odpowiedzi dotyczą bardziej sposobu organizowania zmian niż nieprzekazywania całego świata funkcjom, ponieważ z definicji nie można podzielić segmentu całkowicie niezależnego - ale spróbuj zrobić to najlepiej jak potrafisz w twoim przypadku, co nie jest tylko funkcjonalne, ale także dobre praktyki.
źródło
Sam zdecydowanie zastanowiłbym się, czy dany język ma dostęp do jakiejś formy bazy danych. Większość wydarzeń, które jednocześnie zmieniają stan świata, zostaną po prostu zapisane na dysku i nie wpłyną na obecnego gracza w bieżącym pokoju (poza wyjątkowymi okolicznościami, takimi jak wybuchy lub w MMO, przełączniki otwierające drzwi zdalnie, krzyki innych graczy itp.).
Jako taki, aktualny klient naprawdę musi być świadomy
Room
obiektu i rzeczy, które mają na niego bezpośredni wpływ.noticableEventsOutsideRoom
może być po prostu podklasą, na którąRoom
wpływ miały ostatnie zmiany w bazie danych, a obiekt gry stał się znacznie mniejszy.źródło
update mobs set agro=1 where distance<5
i będę skończone z tym. Może nie jest to najlepsza praktyka, ale pasuje do moich celów. Jeśli chodzi o wyszukiwanie ścieżek za pomocą bazy danych, zawsze można było użyć najkrótszego algorytmu ścieżki Dijkstry ...Prawdziwym rozwiązaniem nie jest zebranie wszystkiego do dużego obiektu świata, a następnie przekazanie go. Zamiast tego zaleca się dokładne określenie rodzaju funkcji, z którą mamy do czynienia. Oto kilka przykładów:
Zły przykład próbuje zmodyfikować istniejący obiekt, ale dobrym przykładem jest próba stworzenia świata z przestrzeni stanu, w której znajduje się gra. Zasadniczo, aby stworzyć świat, musisz znać wszystkie dane wymagane do wybrania właściwego świata.
źródło