“Memoization” Kavramı Nedir?
Bu yazımızda, “memoization nedir?” sorusunu detaylıca ele alarak, bu tekniğini kullanarak fonksiyonların hesaplama sürelerini nasıl optimize edebileceğimizi inceleyeceğiz. Özellikle tekrarlanan hesaplamaların fonksiyonlarımızın performansını nasıl etkilediğini göz önünde bulundurarak, bu tekniğin pratikte nasıl uygulandığına dair anlaşılır örnek üzerinden göstereceğiz.
“Memoization” Nedir?
“Memoization”, özellikle tekrarlı hesaplamaların önlenmesi amacıyla kullanılan bir optimizasyon tekniğidir. Temel prensibi, bir fonksiyonun daha önce aldığı girdilerle ilgili hesapladığı sonuçları bir önbellekte saklamak ve aynı girdilerle fonksiyon tekrar çağrıldığında yeniden çalıştırmak yerine halihazırda önbellekte olan bu sonucu hemen döndürmektir. Bu yaklaşım, fonksiyonun tekrar tekrar aynı hesaplamayı yapmasını önler, böylece işlem süresinden tasarruf edilir.
Zaten “memoization” kelimesi de, “memo“dan türetilmiştir ki bu da “not”, “hafıza” gibi anlamlara gelir; yani daha önce yapılmış bir hesaplamayı “not alıp” saklama fikrini ifade eder. Anlaşılacağı üzere, kelime anlamıyla yaptığı iş net bir şekilde ifade edilmektedir.
Özellikle kaynak israfına yol açan fonksiyon çağrılarında önemli kazanımlar sağlar. Halihazırda uzun süren işlemleri bir kez yaparız ve sonuçlarını saklarız. Tekrar çağrılarında yeniden çalıştırmak yerine sakladığımız sonucu döndürürüz. Böylece, fonksiyonun tekrar tekrar aynı hesaplamaları yapmasını engellemiş oluruz. Böylece performans kazancı yani optimizasyon yapılmış olur.
JavaScript’te “Memoization” Kullanımı
“Memoization” terimi, özünde bir algoritma veya programlama tekniği olduğu için, özellik olarak belirli bir programlama diline özgü değildir. Ancak konuyla alakalı örnekleri biz JavaScript üzerinden göstereceğiz.
İlk olarak “memoization” tekniğini kullanmadığımız bir örneği verelim:
// Kötü olan yöntem
function ekle95(n) {
console.log('hesaplama sıfırdan yapıldı...')
return n + 95;
}
ekle95(5); // hesaplama sıfırdan yapıldı... 100
ekle95(5); // hesaplama sıfırdan yapıldı... 100
ekle95(5); // hesaplama sıfırdan yapıldı... 100
ekle95(10); // hesaplama sıfırdan yapıldı... 105
Görüldüğü üzere, bu ekle95
fonksiyonu her çağrıldığında “hesaplama sıfırdan yapıldı…” mesajını yazdırıyor. Bu, fonksiyonun her seferinde aynı işlemi tekrar tekrar yaptığını gösteriyor. Eğer bu fonksiyon karmaşık bir hesaplama yapıyor olsaydı, bu tür tekrarlanan işlemler performans sorunlarına yol açacaktı.
Sık sık aynı girdilerle çağırdığınızda gereksiz işlem yükü oluşturur. Bu yükü önlemek için “memoization” tekniğini kullanabiliriz. Bakınız:
// Memoization
function memoizedEkle95() {
let cache = {}
return function(n) { // Dikkat: catche nesnesine erişimi var
if (n in cache) {
return cache[n];
} else {
console.log('hesaplama sıfırdan yapıldı...');
cache[n] = n + 95;
return cache[n];
}
}
}
const memoized = memoizedEkle95();
console.log('1:', memoized(5)); // 100 ilk sonuç
console.log('2:', memoized(5)); // 100 --> tekrarlı sonuç
console.log('3:', memoized(5)); // 100 --> tekrarlı sonuç
console.log('4:', memoized(10)); // 105 --> ilk sonuç
console.log('5:', memoized(10)); // 105 --> tekrarlı sonuç
console.log('6:', memoized(5)); // 100 --> tekrarlı sonuç
console.log('4:', memoized(15)); // 110 --> ilk sonuç
Bu kod, memoization tekniğini kullanarak n
sayısına 95 ekleyen bir fonksiyonu optimize eder. Fonksiyon, hesaplamayı daha önce yaptıysa, önbellekten (yani cache
nesnesinden) sonucu alarak doğrudan geri döndürür. Eğer önbellekte bu değer yoksa, hesaplamayı gerçekleştirir, sonucu önbelleğe kaydeder ve sonucu geri döndürür.
Fonksiyonun tekrarlanan çağrıları, ön bellekte sonucun olup olmadığını kontrol ederek ön bellekten sonuç döndürür. Fonksiyon her çağrıldığında öncelikle argümanın önbellekte olup olmadığını kontrol eder. Eğer argüman önbellekte varsa, hesaplama yapmadan zaten hesaplanmış olan sonucu döndürür.
Buna karşılık sonuç ön bellekte yoksa sıfırdan bir hesaplama yapar, ardından bu sonucu ön belleğe alır ve bunu döndürür. Bu sayede, aynı girdiyle fonksiyonun tekrar tekrar çağrılması durumunda oluşacak hesaplama zamanından tasarruf edilir.
Sonuç olarak, memoization tekniği sayesinde tekrarlanan hesaplamaları önleyerek fonksiyonlarımızın performansını büyük ölçüde artırabiliriz. Bu yaklaşım, özellikle karmaşık ve hesaplama yoğunluklu fonksiyonlarda, uygulamalarımızın daha hızlı ve verimli çalışmasını sağlar.