Bir NaN'ı Kovaladığım Hafta
Bir gün eğitim kaybım bir sayı olmaktan çıktı. Yüksek değil, kararsız değil — sadece NaN, bir bilgisayarın cevabı olmayan bir aritmetik yapması istendiğinde kaldırdığı o küçük bayrak. Ve sonra nedenini bulmam bir haftanın büyük kısmını aldı, çünkü belirtinin ortaya çıktığı yer, problemin gerçekte yaşadığı yere hiç yakın değildi.
Var olan en az bilgi veren hata
NaN, bir bakıma, mümkün olan en kötü hata mesajına yakındır, çünkü neredeyse hiç mesaj değildir. Bir çökme en azından bir satırı işaret eder. Bir istisna bir tip ve bir iz taşır. NaN ise sadece sessizce dokunduğu her şeyi zehirler ve devam eder: onu herhangi bir şeye ekleyin NaN elde edersiniz, herhangi bir şeyle çarpın NaN elde edersiniz, ve sıradan kayan noktalı toplamayla, aralarında bir NaN olan bir milyon mükemmel sayının ortalamasını alın bütün ortalama NaN çıkar.
Yani kaybımda yüzeye çıktığında — yüksek sesli, nihai, eğitimi durduran — gerçek kötü değerin nereden girdiği hakkında bana neredeyse hiçbir şey söylemedi. Belirti, sebepten olabileceği kadar uzaktaydı, aralarında uzun bir aritmetik zinciri vardı ve bu zincirin her halkası zehri hiç şikayet etmeden sadakatle ileri taşıyordu.
Bir NaN’ı ayıklamak arkeolojidir
Bu durumda, onu ileriye doğru ayıklamanın temiz bir yolu yoktu. Görebildiğiniz NaN bir hikayenin sonudur; onu başlatan kötü değer daha önce, yukarı akışta bir yerde gerçekleşti ve o zamandan beri kat kat hesaplamadan geçirilerek aklandı. Bu yüzden geriye doğru çalışırsınız. Her aşamaya kontroller eklersiniz — değer burada sonlu mu? burada? burada? — hattı tersten yürüyerek, gerçek bir sayının gerçek-olmayan birine dönüştüğü ilk yeri ararsınız.
Yavaş, gösterişsiz bir iştir ve programlamadan çok kazıya benzer: işlerin ters gittiği tam katmanı bulmak için dönüşüm katmanlarını geriye doğru kazmak. Hayatımdan bir hafta o kazıya gitti.
Suçlu: iki kez uygulanan bir dönüşüm
Sonunda gün yüzüne çıkardığım şey neredeyse hakaret edercesine küçüktü. Ön işlememin bir yerinde, bir değere bir dönüşüm uygulanıyordu — ve sonra, haftalar önce yaptığım bir yeniden düzenleme yüzünden, ona ikinci bir kez uygulanıyordu. Her uygulama kendi başına mükemmel şekilde doğruydu. İlki yapması gereken şeyi tam olarak yaptı. İkincisi zaten dönüştürülmüş değeri aldı ve matematiğin artık takip edemeyeceği bir yere, işlemin tanımlı bir sonucunun olmadığı bir bölgeye itti. Dışarı bir sayı olmayan bir şey çıktı ve aşağı akışa doğru gidip bir hafta sonra ölü bir kayıp olarak yüzeye çıktı.
Hata yanlış bir dönüşüm değildi. Doğru bir dönüşümün bir kez fazla çalışmasıydı, çünkü kodun iki farklı parçasının her biri ondan kendisinin sorumlu olduğuna inanıyordu.
Ön işleme neden tam olarak bu hatayı üretir
Bu tür bir şey kötü şans değildir; ön işleme hatlarına yayılmalarına izin verdiğinizde üretme eğiliminde oldukları şeydir. Veri hazırlamanın bir kod tabanına bulaşma gibi bir yolu vardır — burada biraz yeniden şekillendirme, orada biraz yeniden ölçeklendirme, farklı yerlerde farklı zamanlarda yazılmış farklı işlevler tarafından uygulanmış. Ve aynı dönüşüme iki farklı yoldan ulaşılabildiği an, bir değerin her ikisinden de seyahat edip iki kez dönüştürülme riski artar.
Yolların hiçbiri tek başına yanlış değildir. Onu bu kadar zor görülür kılan şey budur. Hata herhangi bir tek kod parçasında yaşamaz; iki parça arasındaki ilişkide yaşar ve hiçbiri diğerini bilmez.
Çözüm: tek yer, tek kez
Onarım kötü satıra bir yama değildi. Yapısaldı. Dönüşümleri biriktikleri dağınık yerlerden çekip aldım ve tek, açık bir kuralı olan tek, merkezi bir aşamaya koydum: her değer burada, tam olarak bir kez dönüştürülür ve başka hiçbir yerde dönüştürülmez. Ondan sonra, herhangi bir şeyin seyahat edebileceği ikinci bir yol kalmadı. Belirli NaN kayboldu, ama daha da önemlisi, tutarsız uygulamanın gerçekleşebileceği yolların sayısını azalttı — yalnızca bu hatayı düzeltmekle kalmadı, kardeşlerinin ortaya çıkabileceği alanı da küçülttü.
İşte kovalamaya değer türden bir çözüm budur. Tek kötü değeri yamalamak bu NaN’ı ortadan kaldırırdı. Dönüşümü merkezileştirmek bu NaN’ın gelecekteki birçok versiyonunu da çok daha az olası kıldı.
Asıl ders: sebep ile belirti arasındaki mesafeye dikkat edin
Bu hatayı pahalı kılan şey zorluğu değildi. Mesafeydi. Arıza bir yerde ortaya çıktı, sebep tamamen farklı bir yerde oturdu ve onları görünür şekilde bağlayan hiçbir şey yoktu, sadece kötü bir değeri bir uçtan diğerine görevle taşıyan uzun, sessiz bir matematik zinciri dışında.
Bu tür bir mesafeye karşı gerçekten yardımcı olan savunmalar zekâ değildir. Yapı ve kontrollerdir: tehlikeli işlemleri merkezileştirin ki ters gidebilecekleri tek bir yer olsun, ve değerleri sınırlarda doğrulayın — bir sayının üretildiği ana yakın bir yerde sonlu olup olmadığını kontrol edin, bir hafta sonra nihayet patladığında değil. Doğduğu yerde yakalanan kötü bir değer size dakikalara mal olabilir. Patladığı yerde yakalanan aynı kötü değer size bir haftaya mal olabilir.
— Sinyal yok, getiri yok, yatırım tavsiyesi değildir.