Fonksiyonel programlama ve Metin maceraları


14

Bu çoğunlukla FP hakkında teorik bir sorudur, ama benim açımdan göstermek için metin maceraları (eski okul Zork gibi) alacağım. FP ile durumsal bir simülasyonu nasıl modelleyeceğiniz hakkındaki görüşlerinizi bilmek istiyorum.

Metin maceraları gerçekten OOP gerektiriyor gibi görünüyor. Örneğin, tüm "odalar" bir Roomsınıfın örnekleridir , taşıyabileceğiniz şeyler Itemgibi temel bir sınıfa ve arayüzlere sahip Item<Pickable>olabilirsiniz.

FP'de dünya modellemesi farklı çalışır, özellikle oyun ilerledikçe değişmesi gereken bir dünyada değişmezliği zorlamak istiyorsanız (nesneler taşınır, düşmanlar yenilir, puanlama büyür, oyuncu yerini değiştirir). Her Worldşeye sahip tek bir büyük nesne hayal ediyorum : keşfedebileceğiniz odalar, nasıl bağlantılı oldukları, oyuncunun ne taşıdığı, hangi kolların tetiklendiği.

Ben saf bir yaklaşım temelde bu büyük nesneyi herhangi bir fonksiyona geçmek ve onlar tarafından döndürmek (muhtemelen değiştirilmiş) olacağını düşünüyorum. Örneğin, ben moveToRoomalır Worldve World.player.locationyeni odaya değiştirildi ile döndüren bir işlevi var , World.rooms[new_room].visited = Truevb.

Bu daha "doğru" yol olsa bile, bunun uğruna saflığı güçlendiriyor gibi görünüyor. Programlama diline bağlı olarak, bu potansiyel olarak çok büyük Worldnesneyi ileri geri taşımak pahalı olabilir. Ayrıca, her işlevin herhangi bir Worldnesneye erişimi olması gerekebilir . Örneğin, bir odaya su basmış olabileceği için başka bir odada tetiklenen bir kola bağlı olarak erişilebilir veya olmayabilir, ancak oyuncu can yeleği taşıyorsa, yine de girebilir. Bir canavar saldırgan olabilir veya oyuncunun kuzenini başka bir odada öldürüp öldürmediğine bağlı olabilir. O Bu araçlar roomCanBeEnteredişlev erişmesi gereken World.player.invetoryve World.rooms, describeMonstererişim ihtiyaçları World.monstersve böylece temelde, siz (on gerekirtüm yükü çevirin). Bu, özellikle FP'de iyi bir programlama tarzı dışında olsa bile, küresel bir değişken çağırmak için bana gerçekten benziyor.

Bu sorunu nasıl çözersiniz?


4
"Programlama diline bağlı olarak, potansiyel olarak çok büyük olan bu Dünya nesnesini ileri geri taşımak pahalı olabilir." Muhtemelen referans ile geçilecektir. "Ayrıca, her işlevin herhangi bir Dünya nesnesine erişmesi gerekebilir." Her fonksiyonun oyunun tüm durumuna erişmesi gerektiğine inanmakta güçlük çekiyorum .
Doval

2
Bence Chris Marten'in araştırması ilginç olacak, bildirici dillerde etkileşimli kurguların nasıl güzelleştirileceğini göstermeyi amaçlıyor. github.com/chrisamaphone/interactive-lp
Daniel Gratzer

2
Bu bloga , yazarın böyle bir oyunu işlevsel bir şekilde programlamaya yönelik yaklaşımını açıklayan bir göz atmak isteyebilirsiniz . Bu yazı özellikle oldukça önemli.
gallais

3
Bu sorunun @ EricLippert'in Ocaml'de bir Z makinesinin (parçalarının) uygulanması hakkında bir dizi makale yazma kararını etkileyip etkilemediğini merak etmeliyim ...?
Jules

1
@Jules İlgilenenler için bu dizinin başlangıcına bir bağlantı: ericlippert.com/2016/02/01/west-of-house
KChaloux

Yanıtlar:


4

İşlevsel dillerin nesneler yerine veri yapıları ve ayrılmış işlevler kullandığını unutmayın. Örneğin, bir dizi odanız ve bunun yerine dünya olarak envanter öğelerinin bir listesi olurdu.

İdeal olarak, işlevlere verdiğiniz veri miktarını, tüm dünyayı geçmek yerine gerçekten mümkün olduğunca ne kadar gerektirdikleriyle sınırlarsınız (dünyanızdan tek bir alakalı oda çıkardığınızı; elbette tamamen birbirine bağımlı dünyaların zor olabileceğini varsayalım. ayrı). Sonuç, dünya veri yapısına yeniden entegre edilerek yeni bir devlet yaratılacaktı. Durumu kullanmadan modeli modelleyemezsiniz; dediğiniz gibi bazı şeyler doğal olarak mutasyon gerektirir.

En pratik fonksiyonel diller, mutasyonu doğrudan gerçekleştirmenin bir yolunu sağlar (Haskell'deki ST monadını veya Clojure'daki geçici akımları diyelim) veya verimli bir şekilde simüle eder (genellikle veri yapısının değişmeyen kısımlarını yeniden kullanarak (Clojure'un varsayılan veri yapıları burada iyi bir örnektir) ). Her iki şekilde de saflık korunur.

Mutasyona uğraması gereken durum miktarı sınırlı göründüğünden, performans sorunları hakkında çok fazla endişelenmeyeceğim ve (muhtemelen zaten optimize edilmiş) naif fonksiyonel yaklaşımla devam etmeyeceğim.

Gördüğüm farklı bir seçenek, sadece dünyanın bir kısmını bireysel işlevlerinizden değiştirmek için talimatlar döndürmek ve ardından dünyanızı bunlara göre güncellemek olacaktır. Bunu açıklayan bir dizi blog yayını http://prog21.dadgum.com/23.html adresinde bulunabilir .

Bu yanıtların her ikisi de, tüm dünyanızı işlevlere geçirmekten ziyade değişiklikleri nasıl organize edeceğinizle daha fazla ilgilidir, çünkü mükemmel bir şekilde bağımlı olan bir tanım tanımı ile bölümlere ayrılamaz - ancak durumunuzda olabildiğince iyi yapmaya çalışın, sadece işlevsel değil, aynı zamanda iyi bir uygulama.


0

Kendim, kesinlikle söz konusu dilin bir çeşit veritabanına erişme yeteneğine bakacağım. Dünyanın durumunu aynı anda değiştiren olayların çoğu sadece diske kaydedilir ve mevcut odadaki (patlamalar gibi özel koşulların dışında veya MMO'da, kapıları açan anahtarlar) mevcut oyuncuyu etkilemez. uzaktan, diğer oyuncuların bağırmaları vb.).

Bu nedenle, mevcut müşterinin gerçekten sadece Roomnesnenin ve onu doğrudan etkileyen şeylerin farkında olması gerekir. noticableEventsOutsideRoomdaha sonra Roomveritabanındaki son değişikliklerden etkilenen bir alt sınıf olabilir ve oyun nesneniz çok daha küçük hale gelmiştir.


Bu yaklaşımın, yerel olayları (yakındaki çetelerdeki agro gibi) yol bulma veya tetikleme için çok fazla bir şey yapmadığını anlıyorum, ancak geçmişte veritabanlarını kötüye kullandığım biliniyordu ... Muhtemelen sadece bir çağrı gönderir update mobs set agro=1 where distance<5ve onunla yapılır. Belki de bu en iyi uygulama değildir, ama benim amacımdır. Veritabanı yoluyla yol
bulmaya

0

Gerçek çözüm değil Etrafını geçen ardından büyük bir Dünya nesnesine her şeyi toplayarak ve tarafından. Bunun yerine, uğraştığınız işlevin türünü doğru bir şekilde belirtmeniz önerilir. İşte bazı örnekler:

BAD:
   f :: (World, Int) -> World

Good:
   f :: (Int,Int,Int,Int) -> World

Kötü örnek mevcut nesneyi değiştirmeye çalışmaktır, ancak iyi örnek oyununuzun bulunduğu durum uzayından bir dünya yaratmaya çalışmaktır. Temel olarak, bir dünya yaratmak için doğru dünyayı seçmek için gereken tüm verileri bilmeniz gerekir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.