SORU
18 Mart 2013, PAZARTESİ


Haskell sürdürmeye karmaşık devlet

Haskell oldukça büyük bir simülasyon yapmakta olduğunuzu varsayın. Simülasyon ilerledikçe güncelleme olan işletmelerin birçok farklı türleri vardır. Diyelim varlıklar Maymunlar, Filler, Ayılar, vb olarak adlandırılan, örnek uğruna söylüyorlar

Bu varlıklar bakımı Birleşik Devletleri? için tercih edilen yöntem nedir

Düşündüm ve en belirgin ilk yaklaşım oldu bu

mainLoop :: [Monkey] -> [Elephant] -> [Bear] -> String
mainLoop monkeys elephants bears =
  let monkeys'   = updateMonkeys   monkeys
      elephants' = updateElephants elephants
      bears'     = updateBears     bears
  in
    if shouldExit monkeys elephants bears then "Done" else
      mainLoop monkeys' elephants' bears'

Zaten çirkin varlığın her türünü açıkça mainLoop işlev imzası söz geçiriyor. Eğer olsaydı çok kötü olurdu nasıl düşünün, 20 kadar varlık türleri. (20 karmaşık simülasyonları için mantıksız değil.) Bu kabul edilemez bir yaklaşım olduğunu düşünüyorum. Ama grace updateMonkeys gibi işlevleri, ne yaptıklarını çok açık olmasıdır tasarrufu: Onlar Maymun listesini alır ve yeni bir dönüş.

O zaman bir sonraki düşünce tüm devlet tutan büyük bir veri yapısı içine her rulo için, böylece mainLoop imzası temizlik olurdu:

mainLoop :: GameState -> String
mainLoop gs0 =
  let gs1 = updateMonkeys   gs0
      gs2 = updateElephants gs1
      gs3 = updateBears     gs2
  in
    if shouldExit gs0 then "Done" else
      mainLoop gs3

Bazı Devlet bir Monad içinde GameState şal updateMonkeys vb Ara gösteriyor. do. Sorun değil. Bazı fonksiyon bileşimi ile Temizle öneririm yerine. De gayet iyi bence. (BTW, belki bu konuda yanıldığımı çok Haskell ile bir acemi değilim.)

Ama sorun, updateMonkeys gibi işlevleri yazın, imza, faydalı bilgiler verme. Gerçekten onlar mı emin olamazsın. , updateMonkeys açıklayıcı bir isim. ama bu küçük bir teselli. Ben god object pass ve "devlet," geri zorunlu olan bir dünyada olduğumuzu hissediyorum. benim genel güncelleyin dediğimde Başka bir isimle global değişkenler gibi geliyor: bir işlevi varbir şeygenel durum için, sen söyle, ve en iyisi için umut. (Genel Değişkenler ile mevcut olacağını hala bazı eşzamanlılık sorunları zorunlu bir programda önlemek sanırım. Ertesi gün, eşzamanlılık neredeyse tek şey global değişkenler bir sorun değil.)

Bir başka sorun şudur: nesnelerin etkileşim gerekiyor Sanırım. Örneğin, bu gibi bir işlevi var:

stomp :: Elephant -> Monkey -> (Elephant, Monkey)
stomp elephant monkey =
  (elongateEvilGrin elephant, decrementHealth monkey)

Bu eğer filler herhangi bir maymun aralığı paldır küldür olup olmadığını görmek için kontrol ediyoruz çünkü burası updateElephants, çağrılır söylüyorlar. Nasıl zarif bir şekilde bu senaryoda hem maymunlar ve filler değişiklikleri yaymak mı? İkinci örneğimizde, updateElephants alır ve Tanrı bir nesne döndürür, böylece her iki değişiklik de etkisi olabilir. Tanrı, etkili sadece global değişkenler değiştiriyorsunuz nesne. ama bu sadece daha fazla şüpheli halen bulanık sularda yüzüyor ve görüşümü pekiştiriyor: Ve eğer Tanrı bir nesne kullanıyorsanız, değişiklikleri bu tür yaymak istiyorum nasıl emin değilim.

Ne? Elbette birçok program bu sorun için bilinen bazı yaklaşımlar vardır diye tahmin ediyorum bu kadar karmaşık devlet yönetmek gerekiyor.

Sadece karşılaştırma aşkına, burada OOP dünyanın sorunu çözmek olabilir. Monkey, Elephant olacaktır. nesneler. Muhtemelen tüm canlı hayvan kümesi içinde arama yapmak için sınıf yöntemleri olurdu. Belki de ne olursa olsun KİMLİĞİ konuma göre arama yapabilirsin. Veri yapıları temel arama işlevleri sayesinde, öbek üzerinde ayrılmış kalırlar. (GC veya referans sayma varsayıyorum.) Üye değişkenleri her zaman mutasyona uğrar. Herhangi bir sınıfın herhangi bir metodu başka bir sınıf herhangi bir canlı hayvan mutasyona mümkün olacaktır. E. g. Elephant a geçti Monkey nesne sağlığı indirim olur stomp bir yöntem olabilir, ve bunu geçmek gerek kalmazdı hiç

Her oyuncu kendi döngü ve kendi durumuna böylece korur, asla Tanrı bir nesne gerekir., bir Ayrık ya da oyuncu odaklı tasarımı, bu sorunları çözmek oldukça zarif olabilir diğer benzer şekilde: Ve mesaj geçen bir nesnenin faaliyetleri bir sürü geçmeden diğer nesneleri değişiklikleri tetiklemek için geri çağrı yığını sağlar. Henüz Haskell aktörleri üzerine kaşlarını çattı olduğunu daha önce de duymuştum.

CEVAP
18 Mart 2013, PAZARTESİ


Cevap functional reactive programming (CTP). Bu iki kodlama tarzı bir melez: parça devlet yönetimi ve zamana bağlı değerleri. FRP aslında tasarım desenleri bütün bir aile olduğundan, daha spesifik olmak istiyorum: Netwire tavsiye ederim.

Temel fikir çok basittir: çok küçük, kendi kendine yeten her bileşen kendi yerel devlet ile yazmak. Bu her seferinde farklı bir cevap almak ve yerel bir devlet güncelleştirme neden olabilir böyle bir bileşen sorgu çünkü pratikte zamana bağlı değerleri eşdeğerdir. Daha sonra bu bileşenlerin gerçek programını oluşturmak için bir araya.

Bu karmaşık ve verimsiz görünse de aslında sadece normal işlevlerini etrafında çok ince bir tabaka. Tasarım desen Netwire tarafından uygulanan AFRP (Reaktif Fonksiyonel Programlama Arrowized) esinlenmiştir. Muhtemelen kendi adı (WFRP?) hak etmek için yeterince farklı değil. tutorial okumak isteyebilirsiniz.

Her halükarda küçük bir gösteri izler. Bina blokları teller

myWire :: WireP A B

Bir bileşen olarak düşünür. Saat türü-değişen bir değerdirBbu tür zaman-değişen bir değere bağlıdırBirÖrnek Bir simülatör bir parçacık:

particle :: WireP [Particle] Particle

Parçacıklar (örneğin şu anda varolan tüm parçacıkları) ve kendisi bir parçacık bir listesini bağlıdır. Hadi önceden tanımlanmış bir tel (basitleştirilmiş bir türü) kullanın:

time :: WireP a Time

Bu tür zaman-değişen bir değerdirZaman(=Çift). Peki, zamanın kendisi (Tel ağ başladı her sayıldığını 0'dan başlayarak). Başka bir zaman-değişen değer bağlı değildir bu yana istediğini, dolayısıyla polimorfik giriş türü besleyebilirsiniz. Ayrıca sürekli teller (zaman-değişen zamanla değişmez değerleri vardır:

pure 15 :: Wire a Integer

-- or even:
15 :: Wire a Integer

Sadece kategorik kompozisyon kullanın iki kablo bağlamak için:

integral_ 3 . 15

Bu 15 gerçek zamanlı hızı (integral 15 saat) bir saat 3'te başlayan (sürekli entegrasyon) verir. Çeşitli sınıf örneği teller sayesinde birleştirmek için çok kullanışlı. Operatörler düzenli olarak uygulamalı stil veya stil ok. Saat 10'da başlayan bir saat istiyorum ve gerçek zamanlı hız, iki kez?

10   2*time

Ve (0, 0) ile (0, 0) hız ile başlar ve (2, 1) hızlandıran bir parçacık saniyede saniyede ister misin?

integral_ (0, 0) . integral_ (0, 0) . pure (2, 1)

Kullanıcı boşluk ütülerken istatistikleri görüntülemek istiyorsunuz?

stats . keyDown Spacebar <|> "stats currently disabled"

Bu sadece Netwire sizin için neler yapabileceğine dair küçük bir kısmı.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • AFISHAL

    AFISHAL

    7 Mart 2009
  • booba1234

    booba1234

    22 Temmuz 2006
  • Willie D.

    Willie D.

    16 Aralık 2006