İnsanların (bu sitede de) fonksiyonel programlama paradigmasını rutin olarak övtüğünü ve her şeyin değişmez olmasının ne kadar iyi olduğunu vurguladığını okuyorum ve duyuyorum. Özellikle, insanlar bu yaklaşımı C #, Java veya C ++ gibi geleneksel olarak zorunlu OO dillerinde bile, sadece programcıya zorlayan Haskell gibi tamamen işlevsel dillerde önermektedir.
Anlamakta zorlanıyorum, çünkü değişebilirliği ve yan etkileri buluyorum ... uygun. Bununla birlikte, insanların şu anda yan etkileri nasıl kınadıkları ve mümkün olan her yerde onlardan kurtulmanın iyi bir uygulama olduğunu düşünürsek, yetkin bir programcı olmak istiyorsam, paradigmayı daha iyi anlamak için jorneyime başlamam gerektiğine inanıyorum ... Dolayısıyla S.
İşlevsel paradigma ile ilgili problemler bulduğum bir yer, bir nesneye doğal olarak birden fazla yerden başvurulmasıdır. İki örnekle açıklayayım.
İlk örnek boş zamanlarımda yapmaya çalıştığım C # oyunum olacak . Her iki oyuncunun da 4 canavardan oluşan takımlara sahip olduğu ve takımlarından savaş alanına rakip bir canavar gönderebileceği bir canavar gönderebildiği sıra tabanlı web oyunu. Oyuncular ayrıca savaş alanından canavarları hatırlayabilir ve onları takımlarından başka bir canavarla değiştirebilir (Pokemon'a benzer şekilde).
Bu ortamda, tek bir canavara doğal olarak en az 2 yerden atıfta bulunulabilir: bir oyuncunun takımı ve iki "aktif" canavara atıfta bulunan savaş alanı.
Şimdi bir canavara vurulduğunda ve 20 sağlık puanı kaybettiğinde durumu ele alalım. Zorunlu paradigmanın köşeli parantezleri içinde bu canavarın health
alanını bu değişikliği yansıtacak şekilde değiştiriyorum - ve şimdi bunu yapıyorum. Ancak, bu Monster
sınıf değişken ve ilgili fonksiyonları (yöntemleri) saf olmayan yapar, sanırım şu anda kötü bir uygulama olarak kabul edilir.
Kendime bu oyunun kodunu, geleceğin bir noktasında gerçekten bitirme umuduna sahip olmak için idealden daha az bir duruma sahip olma iznini vermiş olmama rağmen, bunun nasıl olması gerektiğini bilmek ve anlamak istiyorum düzgün yazılmış. Bu nedenle: Bu bir tasarım hatasıysa, nasıl düzeltilir?
İşlevsel tarzda, anladığım kadarıyla, bunun yerine bu Monster
nesnenin bir alanı dışında eskisiyle aynı kalmasını sağlıyorum; ve yöntem suffer_hit
eskisini değiştirmek yerine bu yeni nesneyi döndürür. Sonra aynı şekilde Battlefield
, bu canavar hariç tüm alanlarını aynı tutarak nesneyi kopyalarım .
Bu en az 2 zorluk ile gelir:
- Hiyerarşi bu basitleştirilmiş sadece
Battlefield
-> örneğinden çok daha derin olabilirMonster
. Biri hariç tüm alanların bu şekilde kopyalanması ve bu hiyerarşinin sonuna kadar yeni bir nesne döndürülmesi gerekir. Bu, özellikle fonksiyonel programlamanın kazan plakasını azaltması gerektiği için sinir bozucu bulduğum kazan plakası kodu olacaktır. - Bununla birlikte, çok daha ciddi bir sorun, bunun verilerin senkronize olmamasına yol açmasıdır . Alanın aktif canavarı sağlığının azaldığını görecekti; ancak kontrol eden oyuncusundan bahseden aynı canavar
Team
bunu yapmaz. Bunun yerine zorunlu stili benimsediğimde, verilerin her modifikasyonu diğer tüm kod yerlerinden anında görülebilir ve bu gibi durumlarda gerçekten uygun buluyorum - ama bir şeyleri elde etme yolu tam olarak insanların söylediği şeydir emir kipi ile yanlış!- Artık
Team
her saldırının ardından bir yolculuğa çıkarak bu konuyla ilgilenmek mümkün olacak . Bu ekstra bir iş. Bununla birlikte, bir canavar daha sonra daha fazla yerden aniden referans verilebilirse ne olur? Örneğin, bir canavarın zorunlu olarak sahada olmayan başka bir canavara odaklanmasına izin veren bir yetenekle gelirsem (aslında böyle bir yeteneği düşünüyorum)? Ben Will mutlaka immediatelly her saldırıdan sonra da odaklanmış canavarlara bir yolculuğa almayı unutmayın? Bu kod daha karmaşık hale geldikçe patlayacak bir saatli bomba gibi görünüyor, bu yüzden bir çözüm olmadığını düşünüyorum.
- Artık
Aynı soruna çarptığımda daha iyi bir çözüm fikri ikinci örneğimden geliyor. Akademi'de Haskell'de kendi tasarımımızın bir dilinin bir tercümanı yazmamız söylendi. (Ben de FP'nin ne olduğunu anlamaya başlamak zorunda kaldım). Sorun, kapanışları uygularken ortaya çıktı. Bir kez daha aynı kapsama artık birden çok yerden referans verilebilir: Bu kapsamı tutan değişken ve içiçe kapsamların ana kapsamı olarak! Açıkçası, bu kapsamda, kendisine işaret eden referanslardan herhangi biri aracılığıyla bir değişiklik yapılırsa, bu değişikliğin diğer tüm referanslar tarafından da görülebilir olması gerekir.
Birlikte geldiğim çözüm, her bir kapsama bir kimlik atamak ve State
monad'daki tüm kapsamların merkezi bir sözlüğünü tutmaktı . Artık değişkenler, kapsamın kendisinden ziyade yalnızca bağlı oldukları kapsamın kimliğini tutacak ve iç içe kapsamlar, üst kapsamının kimliğini de tutacaktır.
Sanırım aynı yaklaşım benim canavar savaş oyunumda da denenebilir ... Alanlar ve takımlar canavarlara atıfta bulunmaz; bunun yerine merkezi bir canavar sözlüğüne kaydedilen canavarların kimliklerini tutarlar.
Ancak, bir kez daha bu yaklaşımla ilgili bir sorun görüyorum ki, soruna çözüm olarak tereddüt etmeden kabul etmeme engel oluyorum:
Bir kez daha kaynatma plakası kaynağıdır. Tek satırları zorunlu olarak 3 satır yapar: önceden tek bir alanın tek satır yerinde modifikasyonu artık gerektirir (a) Nesneyi merkezi sözlükten almak (b) Değişikliği yapmak (c) Yeni nesneyi kaydetmek merkezi sözlüğe. Ayrıca, referanslar yerine nesnelerin ve merkezi sözlüklerin kimliklerinin tutulması karmaşıklığı artırır. FP karmaşıklığı ve kaynak kodunu azaltmak için ilan edildiğinden , bu yanlış yapıyorum ipuçları.
Ayrıca çok daha şiddetli görünen asecond problemi hakkında yazacaktım: Bu yaklaşımda bellek sızıntıları ortaya çıkıyor . Ulaşılamayan nesneler normalde çöp toplanır. Bununla birlikte, merkezi bir sözlükte tutulan nesneler, bu özel kimliğe başvurmayan hiçbir ulaşılabilir nesne olmasa bile çöp toplanamaz. Teorik olarak dikkatli programlama bellek sızıntılarını önleyebilirken (artık gerekli olmadığında her nesneyi merkezi sözlükten manuel olarak kaldırmaya dikkat edebiliriz), bu hataya eğilimlidir ve programların doğruluğunu arttırmak için FP reklamı yapılır, bu da bir kez daha doğru yol değil.
Ancak, zaman içinde bunun çözülmüş bir problem gibi göründüğünü öğrendim. Java WeakHashMap
bu sorunu çözmek için kullanılabilir sağlar. C # benzer bir olanak sağlar - ConditionalWeakTable
- belgelere göre derleyiciler tarafından kullanılması gerekiyorsa da. Ve Haskell'de System.Mem.Weak var .
Bu sözlükleri saklamak bu soruna doğru işlevsel çözüm mü yoksa göremediğim daha basit bir çözüm var mı? Bu tür sözlüklerin sayısının kolayca ve kötü bir şekilde büyüyebileceğini hayal ediyorum; yani bu sözlüklerin değişmez olması gerekiyorsa, bu çok fazla parametre geçişi veya bunu destekleyen dillerde monadik hesaplamalar anlamına gelebilir , çünkü sözlükler monad'larda tutulacaktır (ama bir kez daha bunu tamamen işlevsel olarak okuyorum bu sözlük çözümü neredeyse tüm kodları State
monad'ın içine yerleştirirken, mümkün olduğunca az kod tek dil olmalıdır ;)
Biraz düşündükten sonra bir soru daha ekleyeceğimi düşünüyorum: Bu sözlükleri oluşturarak ne kazanıyoruz? Zorunlu programlamada yanlış olan şey, birçok uzmana göre, bazı nesnelerdeki değişikliklerin diğer kod parçalarına yayılmasıdır. Bu sorunu çözmek için nesnelerin değişmez olması gerekiyor - tam da bu nedenle, doğru bir şekilde anlarsam, onlarda yapılan değişikliklerin başka bir yerde görünmemesi gerekir. Ama şimdi güncel olmayan veriler üzerinde çalışan diğer kod parçaları hakkında endişeliyim, bu yüzden merkezi sözlükler icat ettim, böylece ... kodun bazı parçalarında bir kez daha değişiklikler diğer kod parçalarına yayılır! Bu nedenle, sözde tüm dezavantajları ile zorunlu bir tarza geri dönmüyoruz, ancak daha fazla karmaşıklık ile mi?
Team
) savaşın sonucunu ve böylece canavarların durumlarını (savaş numarası, canavar varlık kimliği) demetiyle alabilirler.