Burada bazı güzel örnekler var ama değişmezliğin bir tonda yardımcı olduğu bazı kişisel deneyimlere katılmak istedim. Benim durumumda, üst üste binen okuma ve yazmalara paralel olarak sadece kodları güvenle çalıştırabilme ve yarış koşulları hakkında endişelenmemesi umuduyla değişmez eşzamanlı bir eşzamanlı veri yapısı tasarlamaya başladım. John Carmack böyle bir fikir hakkında konuştuğu yerde yapmam için bana ilham verdi. Oldukça basit bir yapı ve bu şekilde uygulanması oldukça önemsiz:
Elbette birkaç tane daha zil ve ıslık çalmakla birlikte, elementleri sabit bir zamanda çıkarabilir ve geri çekilebilir delikleri geride bırakabilir ve bloklar boşalır ve belirli bir değişmez durum için potansiyel olarak serbest kalırsa geri çekilir. Ancak temelde yapıyı değiştirmek için, "geçici" bir sürümü değiştirirsiniz ve eski sürümle temas etmeyen yeni bir değişmez kopya elde etmek için üzerinde yaptığınız değişiklikleri atomik olarak taahhüt edersiniz, yeni sürüm sadece blokların yeni kopyalarını oluşturur. sığ kopyalama ve referansların diğerlerine sayılması sırasında benzersiz hale getirilmesi gerekir.
Ancak, ben bulamadık ookuyuculu amaçlar için kullanışlıdır. Ne de olsa, bir fizik sisteminin aynı anda bir fiziği aynı anda uyguladığı ve bir oyuncu dünyadaki elemanları hareket ettirmeye çalıştığı kavramsal bir problem var. Dönüştürülen verinin hangi değişmez kopyasıyla birlikte gidiyorsunuz, oynatıcının dönüştürdüğü veya fizik sisteminin dönüştürdüğü? Bu yüzden, bu basit kavramsal soruna gerçekten daha basit ve basit bir çözüm bulamadım, zekice daha kolay bir şekilde kilitlenen ve üst üste binen okumaları engelleyen değişmez veri yapılarına sahip olmaktan ve tamponun aynı bölümlerine tıkanmaktan kaçının. Bu, John Carmack'ın oyunlarında nasıl çözüleceğini bulmuş gibi göründüğü bir şey; en azından bir solucan arabası açmadan neredeyse bir çözüm görüyormuş gibi konuşuyor. Bu konuda ona kadar gelmedim. Görebildiğim tek şey, değişkenlerin etrafındaki her şeyi paralel hale getirmeye çalıştığımda sonsuz tasarım soruları. Keşke çabalarımın çoğu, attığı fikirlerle başladığından beri, bir gün beynini alarak geçirebilseydim.
Yine de, bu değişken veri yapısının diğer alanlardaki muazzam değerini buldum . Gerçekten garip olan ve rastgele erişime izin veren görüntüleri saklamak için şimdi bile kullanıyorum, daha fazla talimat gerektiriyor and
(işaretçi yönlendirme katmanıyla birlikte sağa kaydırma ve bit yönünde), ancak aşağıdaki faydaları ele alacağım.
Sistemi Geri Al
Bundan faydalandığım en acil yerlerden biri geri alma sistemi idi. Sistem kodunu geri alma, bölgemdeki (Visual FX endüstrisi) en çok hataya neden olan şeylerden biriydi ve yalnızca üzerinde çalıştığım ürünlerde değil, rakip ürünlerde de (onların geri alma sistemleri de lapa lapa idi) çünkü Düzgün geri alma ve yineleme konusunda endişelenecek veri türleri (özellik sistemi, örgü veri değişiklikleri, birbiri ile değişim gibi mülk temelli olmayan gölgelendirici değişiklikleri, çocuğun ebeveyni değiştirme gibi görüntü hiyerarşisi değişiklikleri, görüntü / doku değişiklikleri, vb. vb.)
Bu nedenle, gerekli geri alma kodunun miktarı muazzamdı; çoğu zaman, geri alma sisteminin durum değişikliklerini kaydetmesi gereken sistemi uygulayan kod miktarına rakip oldu. Bu veri yapısına dayanarak, geri alma sistemini aşağı yukarı çekebildim:
on user operation:
copy entire application state to undo entry
perform operation
on undo/redo:
swap application state with undo entry
Normalde, yukarıdaki kod, sahne verileriniz gigabaytları tamamen kopyalamak için kullandığında büyük ölçüde etkin olmaz. Ancak bu veri yapısı sadece değişmeyen şeyleri kopyalar ve aslında tüm uygulama durumunun değiştirilemez bir kopyasını yeterince ucuza saklar. Şimdi geri alma sistemlerini yukarıdaki kod kadar kolay bir şekilde uygulayabiliyorum ve uygulama durumundaki değişmeyen kısımları daha ucuz, daha ucuz ve daha ucuz hale getirmek için bu değişmez veri yapısını kullanmaya odaklanıyorum. Bu veri yapısını kullanmaya başladığımdan beri, tüm kişisel projelerim sadece bu basit modeli kullanan sistemleri geri alıyor.
Şimdi burada hala bazı ek yükler var. En son ölçtüğümde, uygulama durumunun hiçbirinde değişiklik yapmadan tüm uygulama durumunu sığdırmak yaklaşık 10 kilobayt idi (bu, sahne hiyerarşisinde düzenlendiğinden sahne karmaşıklığından bağımsızdır, bu nedenle kök altında hiçbir şey değişmezse, sadece kök çocuklara inmek zorunda kalmadan sığ kopyalanır). Bu, yalnızca delta depolayan bir geri alma sistemi için ihtiyaç duyulacak 0 bayttan çok uzak. Ancak işlem başına 10 kilobayt geri alma işleminde, her 100 kullanıcı işleminde sadece bir megabayt. Artı, gerekirse, gelecekte de potansiyel olarak onu daha da ezebilirim.
İstisna-Emniyet
Karmaşık bir uygulamayla istisnai güvenlik önemsiz bir mesele değildir. Bununla birlikte, uygulama durumunuz değişmez olduğunda ve yalnızca atomik değişim işlemlerini gerçekleştirmek için geçici nesneler kullanıyorsanız, o zaman kendiliğinden istisnai güvenlidir, çünkü kodun herhangi bir kısmı atılırsa, geçici yeni bir kopyalanabilir kopya vermeden önce atılır . Böylece, her zaman karmaşık bir C ++ kod tabanında bulabilmek için bulduğum en zor şeylerden birini önemsizleştiriyor.
Çok fazla insan sık sık RAII'ye uygun kaynakları sadece C ++ 'da kullanır ve bunun istisnai güvenlik için yeterli olduğunu düşünür. Genellikle, bir işlev genellikle, kapsamı içinde yerel olanların ötesindeki durumlara yan etkilere neden olabileceğinden değildir. Genellikle, bu durumlarda kapsam korumaları ve karmaşık geri alma mantığı ile ilgilenmeye başlamanız gerekir. Bu veri yapısı, fonksiyonlar yan etkilere neden olmadığından sık sık uğraşmama gerek kalmadı. Başvurunun durumunu dönüştürmek yerine dönüştürülen uygulama durumunun değiştirilmiş kopyalarını döndürüyorlar.
Tahribatsız Düzenleme
Tahribatsız düzenleme, asıl kullanıcı verilerine dokunmadan işlemleri temelde katmanlamak / istiflemek / birbirine bağlamaktır (yalnızca giriş verisine dokunmadan giriş verileri ve çıkış verileri). Photoshop gibi basit bir görüntü uygulamasıyla uygulama yapmak önemsizdir ve çoğu işlem yalnızca görüntünün her pikselini dönüştürmek isteyebileceğinden bu veri yapısından çok fazla yararlanamayabilir.
Bununla birlikte, tahribatsız ağ düzenlemesi ile, örneğin birçok işlem çoğu zaman ağın sadece bir bölümünü dönüştürmek ister. Bir işlem sadece bazı köşeleri buraya taşımak isteyebilir. Bir diğeri sadece bazı çokgenleri oraya bölmek isteyebilir. Burada değişmez veri yapısı, sadece küçük bir kısmı değişmiş olarak ağın yeni bir versiyonunu döndürmek için tüm ağın tam bir kopyasını yapma zorunluluğundan kaçınmada bir ton yardımcı olur.
Yan Etkilerin En Aza İndirilmesi
Elinizdeki bu yapılar sayesinde, yan etkilerini en aza indiren ve bunun için çok büyük bir performans cezası vermeden fonksiyon yazmayı kolaylaştırır. Kendimi, bugün biraz yanıltıcı görünmese bile, yan etkilere maruz kalmadan, tüm değişken veri yapılarını değere döndüren daha fazla işlev yazarken buldum.
Örneğin, tipik olarak, bir grup pozisyonu dönüştürme eğilimi, bir matris ve bir nesne listesi kabul etmek ve bunları değişken bir şekilde dönüştürmek olabilir. Bugünlerde kendimi yeni bir nesne listesi döndürerek buluyorum.
Sisteminizde böyle bir yan etkisi olmayan daha fazla fonksiyona sahip olduğunuzda, doğruluğunu sınamanın yanı sıra doğruluğu hakkında düşünmeyi de kesinlikle kolaylaştırır.
Ucuz Kopyaların Yararları
Her neyse, bunlar değişmez veri yapılarından (veya kalıcı veri yapılarından) en fazla yararlandığım yerler. Ayrıca başlangıçta biraz abartılı oldum ve değişmez bir ağaç ve değişmez bağlı bir liste ve değişmez hash tablosu yaptım, ancak zamanla nadiren bunlar için çok fazla kullanım buldum. Ağırlıklı olarak yukarıdaki tıknaz değişmez dizi benzeri kabın en çok kullanımını buldum.
Ayrıca hala değişkenlerle çalışan bir çok kodum var (en azından düşük seviye kod için pratik bir gereklilik buluyorum), ancak asıl uygulama durumu, değişmez bir sahneden içindeki değişken bileşenlere açılan son derece değişken bir hiyerarşidir. Daha ucuz bileşenlerin bazıları hala tam olarak kopyalanmaktadır, ancak kafesler ve görüntüler gibi en pahalı olanlar, yalnızca dönüştürülmesi gereken parçaların kısmi ucuz kopyalarına izin vermek için değişmez yapıyı kullanır.
ConcurrentModificationException
Genelde aynı ipliğin koleksiyonunu aynı ipliğin gövdesinde, aynı ipliğin gövdesinde mutasyona uğratan aynı ipliğin neden olduğu aptly ismine bakınforeach
.