Genel veri yapıları ve genel olarak C yazmak söz konusu olduğunda kod çoğaltma gerekli bir kötülük olup olmadığını merak ediyorum?
C, kesinlikle benim için, C ve C ++ arasında sıçrayan biri olarak. Kesinlikle günlük olarak C ++ 'dan daha önemsiz şeyleri çoğaltırım, ama kasıtlı olarak ve mutlaka "kötü" olarak görmüyorum çünkü en azından bazı pratik faydalar var - bence her şeyi düşünmek bir hata kesinlikle "iyi" ya da "kötü" gibi - hemen hemen her şey bir değiş tokuş meselesidir. Bu ödünleşimleri net bir şekilde anlamak, gözden geçirmedeki pişmanlık verici kararlardan kaçınmamak ve sadece "iyi" veya "kötü" olarak etiketlemek genellikle bu incelikleri göz ardı eder.
Diğerlerinin işaret ettiği gibi problem C'ye özgü olmasa da, jenerikler için makrolardan veya boş işaretçilerden daha şık bir şey olmaması, önemsiz olmayan OOP'nin garipliği ve C standart kütüphanesinde kap yoktur. C ++ 'da, kendi bağlantılı listelerini uygulayan bir kişi, öğrenci olmadıkça neden standart kütüphaneyi kullanmadığını isteyen öfkeli bir grup olabilir. C programcısının en azından günlük olarak bu tür şeyleri yapabilmesi beklendiğinden, C'de, uykunuzda zarif bir bağlantılı liste uygulaması yapamıyorsanız, öfkeli bir çete davet edersiniz. O' Linus Torvalds'ın, dili anlayan ve "iyi bir tadı" olan bir programcıyı değerlendirmek için bir ölçüt olarak SLL arama ve kaldırma uygulamasını SLL arama ve kaldırma uygulamasını kullandığı bazı tuhaf saplantılar nedeniyle değil. Çünkü C programcılarının bu mantığı kariyerinde binlerce kez uygulaması gerekebilir. C için bu durumda, yeni bir aşçının becerilerini değerlendirerek, en azından her zaman yapmaları gereken temel şeylere hakim olup olmadıklarını görmek için biraz yumurta hazırlamalarını sağlayan bir şef gibi.
Örneğin, muhtemelen bu temel "dizine alınmış ücretsiz liste" veri yapısını, bu tahsis stratejisini kullanan her site için yerel olarak C üzerinde bir düzine kez uyguladım (bir kerede bir düğümü tahsis etmekten ve belleği yarıya indirmek için neredeyse tüm bağlantılı yapılarım 64 bit bağlantıların maliyeti):
Ancak C'de, yalnızca, bu realloc
diziyi kullanan yeni bir veri yapısı uygulanırken, serbest bırakılabilir bir diziye çok az miktarda kod alır ve ücretsiz bir listeye indeksli bir yaklaşım kullanarak bir miktar bellek biriktirir.
Şimdi aynı şey C ++ 'da uygulandı ve orada sadece bir kez bir sınıf şablonu olarak uyguladık. Ancak C ++ tarafında yüzlerce kod satırı ve yüzlerce kod satırını da kapsayan bazı dış bağımlılıklar ile çok, çok daha karmaşık bir uygulama. Ve çok daha karmaşık olmasının ana nedeni, T
olası herhangi bir veri türü olabileceği fikrine karşı kodlamak zorunda olduğumdur . Herhangi bir zamanda (standart kütüphane kaplarında olduğu gibi açıkça yapmak zorunda olduğum yok ederken), hafızayı ayırmak için uygun hizalamayı düşünmek zorunda kaldım.T
(Neyse ki bu C ++ 11'den itibaren daha kolay olsa da), önemsizce inşa edilemez / yıkılabilir olabilir (yeni ve manuel dtor çağrıları gerektirir), her şeyin ihtiyaç duymayacağı, ancak bazı şeylerin ihtiyaç duyacağı yöntemler eklemeliyim, ve hem değişebilir hem de salt okunur (sabit) yineleyiciler, vb. yineleyiciler eklemeliyim.
Büyüyen Diziler Roket Bilimi Değil
C ++ 'da insanlar bunu std::vector
ölüme göre optimize edilmiş bir roket bilim insanının çalışması gibi ses çıkarırlar, ancak sadece realloc
bir geri itme ile dizi kapasitesini arttırmak için kullanılan belirli bir veri türüne kodlanmış dinamik bir C dizisinden daha iyi performans göstermezler. düzine kod satırı. Aradaki fark, sadece büyüyebilen rastgele erişimli bir dizinin standarda tam uyumlu olmasını sağlamak için çok karmaşık bir uygulama gerektiriyor, eklenmemiş öğeler üzerinde ctors çağırmaktan kaçınmak, istisna açısından güvenli, hem const hem de const olmayan rasgele erişim yineleyicileri sağlamak, kullanım türü bazı entegre tipler için doldurma cetvellerini aralık ctorlarından ayırma özellikleriT
, potansiyel olarak POD'lara tip özelliklerini vb. kullanarak farklı davranın. Bu noktada, gerçekten de, sadece büyümeye hazır bir dinamik dizi yapmak için çok karmaşık bir uygulamaya ihtiyacınız var, ancak sadece akla gelebilecek her olası kullanım durumunu ele almaya çalıştığı için. Artı tarafta, hem POD'ları hem de önemsiz olmayan UDT'leri gerçekten depolamanız gerekiyorsa, herhangi bir uyumlu veri yapısında çalışan genel yineleyici tabanlı algoritmalar için kullanmanız gerekiyorsa, tüm bu ekstra çabadan çok fazla kilometre alabilirsiniz, istisna işleme ve RAII'den faydalanın, en azından bazen std::allocator
kendi özel ayırıcı vb. ile geçersiz kılın vb. Ne kadar fayda sağladığınızı düşündüğünüzde kesinlikle standart kütüphanede işe yarar.std::vector
onu kullanan insanların tüm dünyasında vardı, ama bu tüm dünya ihtiyaçlarını hedeflemek için tasarlanmış standart kütüphanede uygulanan bir şey için.
Çok Özel Kullanım Durumlarını İşleme Daha Basit Uygulamalar
Sadece "endeksli ücretsiz listem" ile çok özel kullanım durumlarını ele almanın bir sonucu olarak, bu ücretsiz listenin C tarafında bir düzine kez uygulanmasına ve sonuç olarak bazı önemsiz kodların çoğaltılmasına rağmen, muhtemelen daha az kod yazdım C ++ 'da yalnızca bir kez uygulamak zorunda olduğumdan bir düzine kez daha uygulamak için C ve toplamda bu C ++ uygulamasını korumak zorunda olduğumdan daha fazla zaman harcamak zorunda kaldım. C tarafının bu kadar basit olmasının ana nedenlerinden biri, bu tekniği her kullandığımda genellikle C'deki POD'larla çalışıyorum ve genellikle daha fazla işleve ihtiyacım yokinsert
veerase
bunu yerel olarak uyguladığım belirli sitelerde. Temelde sadece C ++ sürümünün sağladığı işlevselliğin en telafi alt kümesini uygulayabilirim, çünkü çok özel bir kullanım için uyguladığımda ne yaptığım ve tasarım gerekmediği hakkında çok daha fazla varsayım yapmakta özgürüm durum.
Şimdi C ++ sürümü çok daha hoş ve tipte güvenli, ancak yine de istisna-güvenli ve çift yönlü yineleyici uyumlu, örneğin bir genel, yeniden kullanılabilir uygulamanın gelmesi gibi maliyetler uygulamak ve uygulamak için büyük bir PITA oldu bu durumda tasarruf ettiğinden daha fazla zaman. Ve genelleştirilmiş bir şekilde uygulama maliyetinin büyük bir kısmı sadece önceden değil, tekrar tekrar her gün tekrarlanan ödeme süreleri gibi şeyler şeklinde boşa harcanmaktadır.
C ++ 'a Saldırı Değil!
Ama bu C ++ 'a bir saldırı değil çünkü C ++' ı seviyorum, ancak veri yapıları söz konusu olduğunda, C ++ 'ı özellikle uygulamak için çok fazla zaman harcamak istediğim önemsiz olmayan veri yapıları için tercih etmeye geldim. çok genel bir yol, tüm olası türlere karşı istisna-güvenli hale getirmeT
hale getirme, standart uyumlu ve yinelenebilir hale getirme, vb., bu tür ön maliyetlerin gerçekten bir ton kilometre şeklinde ödediği.
Ancak bu aynı zamanda çok farklı bir tasarım zihniyetini de teşvik eder. C ++ çarpışma tespiti için bir Octree yapmak istiyorsanız, ben n. Dereceye genellemek için bir eğilim var. Sadece indeksli üçgen kafesleri depolamak istemiyorum. Neden parmak uçlarımda süper güçlü bir kod oluşturma mekanizması olduğunda çalışabileceği tek bir veri türüyle sınırlandırmalıyım ki bu, çalışma zamanında tüm soyutlama cezalarını ortadan kaldırıyor? Prosedür kürelerini, küpleri, vokselleri, NURB yüzeylerini, nokta bulutlarını vb. Vb. Saklamasını ve her şey için iyi yapmaya çalışmasını istiyorum, çünkü parmaklarınızın ucunda şablonlar olduğunda bu şekilde tasarlamak istemek cazip geliyor. Çarpışma tespiti ile sınırlamak bile istemeyebilirim - ışın izleme, toplama vb. C ++ başlangıçta "sorta kolay" görünmesini sağlar bir veri yapısını nnci derecede genelleştirmek. Ve ben böyle bir uzamsal indeksleri C ++ ile tasarlıyordum. Onları tüm dünyanın açlık ihtiyaçlarını karşılayacak şekilde tasarlamaya çalıştım ve karşılığında aldığım şey, akla gelebilecek tüm olası kullanım durumlarına karşı dengelemek için son derece karmaşık bir kod içeren bir "tüm işlemlerin krikosu" idi.
Ne kadar tuhaf olsa da, yıllar içinde C'de uyguladığım uzamsal dizinlerden daha fazla yeniden kullanıldım ve C ++ hatası yok, ama sadece dilin beni cezbedeceği şeyde benim. C'de bir oktree gibi bir şeyi kodladığımda, sadece puanlarla çalışmasını sağlama ve bununla mutlu olma eğilimim var, çünkü dil, onu nci dereceye kadar genelleştirmeyi denemeyi çok zorlaştırıyor. Ancak bu eğilimler nedeniyle, yıllar boyunca aslında daha verimli ve güvenilir ve bazı görevler için gerçekten uygun olan şeyleri tasarlama eğilimindeydim, çünkü bunlar nci dereceye kadar genel olmakla uğraşmıyorlar. Tüm esnafların krikosu yerine özel bir kategoride as haline gelirler. Yine bu C ++ hatası değil, sadece C yerine aksine kullandığım insan eğilimleri geliyor.
Her neyse, her iki dili de seviyorum ama farklı eğilimler var. CI'de yeterince genelleme eğilimi yoktur. C ++ 'da çok fazla genelleme eğilimim var. İkisini de kullanmak kendimi dengelememe yardımcı oldu.
Genel uygulamalar bir norm mu yoksa her kullanım durumu için farklı uygulamalar mı yazıyorsunuz?
Bir diziden veya kendi kendini yeniden tahsis eden bir diziden düğümleri kullanarak tek başına bağlı 32 bit dizinli listeler gibi önemsiz şeyler için ( std::vector
C ++ 'da ) veya diyelim ki, noktaları saklayan ve daha fazlasını yapmayı amaçlayan bir oktree için, t Herhangi bir veri türünü saklama noktasına genelleme yapmak zahmetine girer. Bunları belirli bir veri türünü saklamak için uyguluyorum (yine de soyut olabilir ve bazı durumlarda işlev işaretçileri kullanabilir, ancak en azından statik polimorfizmle ördek tiplemesinden daha spesifik olabilir).
Ve sağlanan durumlarda biraz fazlalık ile mükemmel bir şekilde mutluyum bunu iyice test etmem , . Birim testi yapmazsam, artıklık hataları çok daha rahatsız edici hissetmeye başlar, çünkü hataları çoğaltabilecek gereksiz kodlarınız olabilir, örneğin Sonra yazdığınız kod türünün tasarım değişikliklerine ihtiyaç duyması muhtemel olmasa bile, değişikliklere ihtiyaç duyabilir çünkü bozuldu. Neden olarak yazdığım C kodu için daha kapsamlı birim testleri yazma eğilimindeyim.
Önemsiz şeyler için, genellikle C ++ için ulaştığımda, ancak C'de uygulayacak olsaydım void*
, sadece işaretçiler kullanmayı düşünürdüm , belki de her öğe için ne kadar bellek copy/destroy
ayıracağını ve muhtemelen işlev işaretleyicilerini bilmek için bir tür boyutunu kabul ederdim önemsiz bir şekilde oluşturulamaz / imha edilemezse verileri derin bir şekilde kopyalayıp yok etmek. Çoğu zaman rahatsız etmiyorum ve en karmaşık veri yapılarını ve algoritmaları oluşturmak için çok fazla C kullanmıyorum.
Belirli bir veri türüyle yeterince sık bir veri yapısı kullanırsanız, yalnızca bit ve void*
baytlarla ve işlev işaretleyicileriyle çalışan ve örneğin tür güvenliğini C sarmalayıcısı aracılığıyla yeniden düzenlemek için, tür güvenli bir sürümü de sarabilirsiniz .
Örneğin karma harita için genel bir uygulama yazmaya çalışabilirdim, ama her zaman sonuçların dağınık olduğunu düşünüyorum. Ayrıca, sadece bu özel kullanım durumu için özel bir uygulama yazabilir, kodu açık ve okunması ve hata ayıklaması kolay tutabilirim. İkincisi elbette bazı kodların çoğaltılmasına yol açacaktır.
Karma tablolar bir tür iffy'dir, çünkü tablonun otomatik olarak kendi başına büyümesini veya tablo boyutunu Açık adresleme veya ayrı zincirleme vb. kullanın tam olarak bu ihtiyaçlara göre ayarlandığında fazlalıklı olmayın. En azından yerel olarak bir şey uygularsam kendime verdiğim mazeret bu. Değilse, yukarıda açıklanan yöntemi void*
ve işlev işaretleyicilerini bir şeyleri kopyalamak / yok etmek ve genelleştirmek için kullanabilirsiniz.
Genellikle çok genelleştirilmiş veri yapısını yenmek için çok çaba veya çok kod almaz eğer sizin alternatif tam kullanım alanına son derece dar geçerlidir. Örnek olarak, malloc
her bir düğüm için (birçok düğüm için bir grup belleği bir araya toplamanın aksine) kullanma performansını bir kez ve kesinlikle çok, çok kesin bir kullanım durumu için tekrar ziyaret etmek zorunda kalmamanız kesinlikle önemsizdir. daha yeni uygulamalar malloc
ortaya çıktığında bile . Onu yenmek ve hayatınızın büyük bir bölümünü genelliğine uymak istiyorsanız onu güncellemek ve güncel tutmak için ayırmanız daha az karmaşık bir kodlama yapmak bir ömür sürebilir.
Başka bir örnek olarak, Pixar veya Dreamworks tarafından sunulan VFX çözümlerinden 10 kat daha hızlı veya daha fazla çözüm uygulamayı genellikle çok kolay buldum. Uykumda yapabilirim. Ama bunun nedeni, uygulamalarımın çok daha üstün olduğu için değil. Çoğu insan için düpedüz aşağıdırlar. Sadece çok, çok özel kullanım durumlarım için üstündürler. Sürümlerim Pixar veya Dreamwork'lerden çok daha az genel olarak uygulanabilir. Çözümleri aptalca basit çözümlerime kıyasla kesinlikle mükemmel olduğu için gülünç derecede haksız bir karşılaştırma, ama bu bir nokta. Karşılaştırmanın adil olması gerekmez. İhtiyacınız olan tek şey çok spesifik birkaç şeyse, ihtiyacınız olmayan şeylerin sonsuz bir listesini işlemek için bir veri yapısı yapmanız gerekmez.
Homojen Bitler ve Baytlar
C'de sömürülecek bir şey, bu tür bir doğal güvenlik eksikliğine sahip olduğundan, bitlerin ve baytların özelliklerine dayanarak işleri homojen bir şekilde saklama fikri. Bellek ayırıcı ve veri yapısı arasında bir bulanıklık daha var.
Ancak bir grup değişken boyutlu şeyi, hatta bir polimorfik gibi ve sadece değişken boyutlu olabilecek şeyleri depolamak verimli bir şekilde yapmak zordur. Değişken boyutta olabileceği varsayımıyla gidemez ve bunları basit bir rasgele erişimli kapta bitişik olarak saklayamazsınız, çünkü bir öğeden diğerine alma adımı farklı olabilir. Sonuç olarak, hem köpekleri hem de kedileri içeren bir listeyi saklamak için 3 ayrı veri yapısı / ayırıcı örneği (biri köpekler, biri kediler için, diğeri polimorfik baz işaretçileri veya akıllı işaretçiler listesi için veya daha kötüsü kullanmanız gerekebilir. , her bir köpeği ve kediyi genel amaçlı bir ayırıcıya ayırın ve onları hafızanın her tarafına dağıtın), bu da pahalı hale gelir ve çoğaltılmış önbellek özlemlerinden payını alır.Dog
Cat
Bu nedenle, C'de kullanılacak bir strateji, azaltılmış tip zenginliği ve güvenliği olmasına rağmen, bit ve bayt düzeyinde genellemektir. Bir fonksiyon işaretçi tablosuna aynı sayıda bit ve bayt, aynı alanlara, aynı işaretçiye sahip olduğunu Dogs
ve Cats
gerekli olduğunu varsayabilirsiniz . Ancak karşılığında daha az veri yapısını kodlayabilirsiniz, ancak en önemlisi, tüm bunları verimli ve bitişik olarak saklayabilirsiniz. Bu durumda köpeklere ve kedilere analog sendikalar gibi davranıyorsunuz (ya da aslında bir birlik kullanabilirsiniz).
Ve bu, güvenliği yazmak için büyük bir maliyet getirmektedir. C'de her şeyden daha fazla özlediğim bir şey varsa, bu tür güvenlik. Yapıların sadece ne kadar bellek ayırdığını ve her veri alanının nasıl hizalandığını gösteren montaj seviyesine yaklaşıyor. Ama bu aslında C'yi kullanmamın bir numaralı sebebi Gerçekten bellek düzenlerini kontrol etmeye çalışıyorsanız ve her şeyin nerede tahsis edildiği ve her şeyin birbirine göre depolandığı yerlerde, genellikle bitler düzeyinde şeyler düşünmeye yardımcı olur ve bayt ve belirli bir sorunu çözmek için ne kadar bit ve bayt gerekir. Orada C tipi sistemin dilsizliği aslında bir handikaptan ziyade faydalı olabilir. Genellikle bununla başa çıkmak için çok daha az veri türü elde edilir,
Hayali / Görünür Çoğaltma
Artık gereksiz bile olmayan şeyler için "çoğaltma" yı gevşek bir anlamda kullanıyorum. İnsanların "tesadüfi / belirgin" çoğaltma gibi terimleri "gerçek çoğaltma" dan ayırdığını gördüm. Gördüğüm gibi, birçok durumda böyle net bir ayrım yok. Ayrımı daha çok "potansiyel teklik" ve "potansiyel çoğaltma" gibi buluyorum ve her iki şekilde de gidebilir. Genellikle tasarımlarınızın ve uygulamalarınızın nasıl gelişmesini istediğinize ve belirli bir kullanım durumu için ne kadar mükemmel bir şekilde uyarlanacağına bağlıdır. Ama sık sık ne kod çoğaltma gibi görünebilir daha sonra birkaç iyileştirme yineleme sonra artık gereksiz olduğu ortaya çıktı.
Kullanarak realloc
, basit bir büyütülebilir dizi uygulaması almak , analog eşdeğer std::vector<int>
. Başlangıçta, örneğin std::vector<int>
C ++ ile kullanmak gereksiz olabilir . Ancak, ölçüm yoluyla, yığın tahsisine gerek kalmadan on altı 32 bit tamsayının eklenmesine izin vermek için önceden 64 bayt önceden konumlandırmanın yararlı olabileceğini görebilirsiniz. Şimdi artık gereksiz değil, en azından ile std::vector<int>
. Ve sonra diyebilirsiniz ki, "Ama bunu sadece yeni bir genelleme yapabilirdim SmallVector<int, 16>
ve siz de yapabilirsiniz. Ama diyelim ki yararlı buluyorsunuz, çünkü bunlar çok küçük, kısa ömürlü diziler için yığın dağılımındaki dizi kapasitesini dört katına çıkarmak için 1,5 oranında (kabacavector
dizi kullanımı her zaman iki güç olarak kabul edilir. Şimdi konteyneriniz gerçekten farklı ve muhtemelen bunun gibi bir konteyner yok. Ve belki de bu tür davranışları, önceden konumlandırma ağırlığını özelleştirmek, yeniden konumlandırma davranışını özelleştirmek vb. kodu.
Ayrıca, 256 bit hizalanmış ve yastıklı bellek ayıran, AVX 256 talimatları için özel olarak POD depolayan, yaygın küçük giriş boyutları için yığın tahsisini önlemek için 128 bayt önceden konumlandıran, kapasiteyi iki katına çıkaran bir veri yapısına ihtiyacınız olan bir noktaya bile ulaşabilirsiniz. ve dizi boyutunu aşan ancak dizi kapasitesini aşmayan sondaki öğelerin güvenli bir şekilde üzerine yazılmasına olanak tanır. Bu noktada, az miktarda C kodunu kopyalamaktan kaçınmak için hala bir çözüm bulmaya çalışıyorsanız, programlama tanrılarının ruhunuza merhameti olabilir.
Bu nedenle, başlangıçta gereksiz görünmeye başlayan şeyin büyümeye başladığı, aynı zamanda, belirli bir kullanım örneğini, tamamen benzersiz ve gereksiz olmayan bir şeye daha iyi ve daha iyi ve daha iyi uyacak şekilde uyarladığınız zamanlar da vardır. Ancak bu sadece onları belirli bir kullanım durumuna mükemmel bir şekilde uyarlayabileceğiniz şeyler için. Bazen sadece amacımız için genelleştirilmiş "iyi" bir şeye ihtiyacımız var ve orada çok genelleştirilmiş veri yapılarından en iyi şekilde faydalanıyorum. Ancak, belirli bir kullanım durumu için mükemmel bir şekilde yapılan istisnai şeyler için, "genel amaç" ve "amacım için mükemmel yapılmış" fikri çok geçimsiz olmaya başlar.
POD'lar ve İlkeller
Şimdi C'de, POD'ları ve özellikle de ilkelleri mümkün olduğunda veri yapılarına depolamak için sıklıkla bahaneler buluyorum. Bu bir anti-desen gibi görünebilir ama aslında yanlışlıkla C ++ daha sık yapmak için şeyler türleri üzerinde kod sürdürülebilirliğini artırmak konusunda yararlı buldum.
Basit bir örnek staj yapmak kısa dizeler (tipik olarak arama anahtarları için kullanılan dizelerde olduğu gibi - çok kısa olma eğilimindedir). Neden boyutları çalışma zamanında değişen tüm bu değişken uzunluklu dizelerle uğraşmak, önemsiz olmayan inşaat ve yıkımı ima ediyor (çünkü ayırmayı ve ücretsiz olarak yığınlandırmamız gerekebilir)? Bu şeyleri, yalnızca dize stajyerliği için tasarlanmış bir iş parçacığı için güvenli trie veya karma tablo gibi merkezi bir veri yapısında depolamaya ve daha sonra eski int32_t
ya da düz bir dizeye bakın :
struct IternedString
{
int32_t index;
};
... karma tablolarımızda, kırmızı-siyah ağaçlarımızda, listelerimizi atladığımızda vb. sözlüksel sıralamaya ihtiyacımız yoksa? Şimdi 32-bit tamsayılarla çalışmak için kodladığımız diğer tüm veri yapılarımız şimdi sadece 32-bit olan bu interned dize anahtarlarını saklayabilir ints
. Ve en azından kullanım durumlarımda buldum (ışın izleme, ağ işleme, görüntü işleme, parçacık sistemleri, komut dosyası dillerine bağlanma, düşük seviyeli çok iş parçacıklı GUI kiti uygulamaları vb. düşük seviyeli şeyler, ancak bir işletim sistemi kadar düşük seviyeli değil), kodun tesadüfen daha verimli ve daha basit hale gelmesi, sadece endeksleri bu tür şeylere depolamasıdır. Bu yüzden sık sık çalışıyorum, diyelim ki zamanın% 75'i, sadece int32_t
vefloat32
önemsiz olmayan veri yapılarımda veya sadece aynı boyutta (neredeyse her zaman 32 bit) şeyleri depolamak.
Doğal olarak durumunuz için geçerliyse, ilk etapta çok azıyla çalışacağınız için farklı veri türleri için bir dizi veri yapısı uygulamasından kaçınabilirsiniz.
Test ve Güvenilirlik
Sunduğum son şey ve herkes için olmayabilir, bu veri yapıları için testler yazmayı tercih etmek. Bir şeyde onları gerçekten iyi yap. Ultra güvenilir olduklarından emin olun.
Bazı küçük kod çoğaltmaları bu durumlarda çok daha affedilebilir hale gelir, çünkü kod çoğaltma yalnızca çoğaltılan kodda basamaklı değişiklikler yapmanız gerektiğinde bir bakım yüküdür. Bu gereksiz kodun değişmesinin başlıca nedenlerinden birini, son derece güvenilir olduğundan ve yapması gerekmeyen şeyler için gerçekten uygun olduğundan emin olarak ortadan kaldırırsınız.
Estetik anlayışım yıllar içinde değişti. Bir kitaplık nokta ürün uygulamak veya başka bir zaten uygulanmış bazı önemsiz SLL mantığı görüyorum çünkü artık tedirgin değil. Sadece işler kötü test edildiğinde ve güvenilmez olduğunda sinirlenirim ve bunu çok daha üretken bir zihniyet buldum. Gerçekten çoğaltılmış kod ile çoğaltılmış hata kod kodları ile ele ve birçok hataya eğilimli basamaklı bir değişiklik haline bir merkezi yere önemsiz bir değişiklik olması gereken kopyala ve yapıştır kodlama en kötü durumları gördük. Ancak o zamanların çoğu, kötü testlerin, kodun ilk etapta yaptığı işte güvenilir ve iyi hale gelememesinin sonucuydu. Daha önce buggy eski kod tabanlarında çalışırken, zihnim tüm kod çoğaltma biçimlerini hataları çoğaltma ve basamaklı değişiklikler gerektirme olasılığı çok yüksek olarak ilişkilendirdi. Yine de, bir şeyi son derece iyi ve güvenilir bir şekilde yapan bir minyatür kütüphane, burada ve orada gereksiz görünen bir kod olsa bile gelecekte değişmek için çok az neden bulacaktır. Çoğaltma kalitesizlik ve test eksikliğinden daha fazla beni rahatsız ettiğinde önceliklerim geri döndü. Bu ikinci şeyler en öncelikli konu olmalıdır.
Minimalizm İçin Kod Çoğaltma?
Bu kafamda ortaya çıkan komik bir düşünce, ancak kabaca aynı şeyi yapan bir C ve C ++ kütüphanesi ile karşılaşabileceğimiz bir durumu düşünün: her ikisi de kabaca aynı işlevselliğe, aynı miktarda hata işlemeye sahip, biri önemli değil Ve en önemlisi, her ikisi de yetkin bir şekilde uygulanmış, iyi test edilmiş ve güvenilirdir. Ne yazık ki burada yan yana karşılaştırmaya hiç yakın bir şey bulamadığım için burada varsayımsal olarak konuşmalıyım. Ancak bu yan yana karşılaştırmaya şimdiye kadar bulduğum en yakın şey, C kütüphanesinin genellikle C ++ eşdeğerinden (bazen kod boyutunun 1 / 10'u) çok daha küçük olmasına neden oldu.
Ve bunun nedeninin, bir problemi, tam bir kullanım durumu yerine en geniş kullanım durumlarını ele alan genel bir şekilde çözmek için yüzlerce ila binlerce satırlık kod gerektirebileceğine inanıyorum. düzine. Gerekliliğe rağmen ve C standart kütüphanesinin standart veri yapıları sağlama konusunda uçsuz bucaksız olmasına rağmen, aynı problemleri çözmek için genellikle insan elinde daha az kod üretilmesine neden olur ve bence bu öncelikle bu iki dil arasındaki insan eğilimlerindeki farklılıklara Biri çok özel bir kullanım durumuna karşı şeyleri çözmeyi teşvik eder, diğeri en geniş kullanım alanına karşı daha soyut ve genel çözümleri teşvik etme eğilimindedir, ancak bunların sonucu yoktur. '
Geçen gün github üzerinde birinin raytracer bakıyordum ve C ++ 'da uygulandı ve bir oyuncak raytracer için çok kod gerekli. Ve koda bakarak o kadar zaman harcamadım ama orada bir raytracer'ın gerekenden çok daha fazla yol alan genel amaçlı yapıların bir yükü vardı. Ve bu kodlama tarzını tanıyorum çünkü C ++ 'ı bir tür süper aşağıdan yukarıya tarzda aynı şekilde kullanıyordum, ilk önce hemen ötesine geçen çok genel amaçlı veri yapılarından oluşan tam bir kütüphane oluşturmaya odaklandım. eldeki sorun ve daha sonra asıl sorunla mücadele ikinci. Ancak bu genel yapılar, burada ve orada bir miktar fazlalığı ortadan kaldırabilir ve yeni bağlamlarda yeniden kullanımın tadını çıkarabilirken, karşılığında, gereksiz kod / işlevsellikten oluşan bir tekne yükü ile biraz fazlalık alışverişi yaparak projeyi büyük ölçüde şişiriyorlar ve ikincisinin bakımı öncekinden daha kolay değildir. Aksine, denge tasarım kararlarını mümkün olan en geniş ihtiyaç yelpazesine karşı sıkıştırmak zorunda olan genel bir şeyin tasarımını sürdürmek zor olduğundan, çoğu zaman sürdürülmesi daha zor olur.