Memoization 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, tekrarlı hesaplamaların önüne geçmek için kullanılan bir optimizasyon tekniğidir. Temel mantığı, bir fonksiyonun belirli girdiler için hesapladığı sonuçları bir önbellekte saklamak ve aynı girdilerle fonksiyon tekrar çağrıldığında bu önbellekteki sonucu doğrudan döndürmektir. Böylece, aynı hesaplamanın tekrar tekrar yapılması engellenir ve işlem süresinden tasarruf edilir.
Kelimenin kökenine baktığımızda, “memoization” ifadesinin “memo” kelimesinden türetildiği görülür. “Memo” kelimesi, “not almak” ya da “hafızaya kaydetmek” anlamına gelir. Bu da, daha önce yapılan bir hesaplamayı saklama fikrini açıkça ifade eder. Yani kelimenin anlamı, tekniğin çalışma prensibiyle doğrudan ilişkilidir.
Memoization, özellikle kaynak tüketimine neden olan karmaşık ve tekrarlı fonksiyon çağrılarında büyük avantajlar sağlar. Zaman alan bir işlemi yalnızca bir kez yapar ve sonucunu saklarsınız. Sonraki çağrılarda aynı girdiler için işlemi yeniden çalıştırmak yerine, saklanan sonucu hızlı bir şekilde döndürürsünüz. Bu yaklaşım, hem işlem süresini kısaltır hem de sistem kaynaklarını daha verimli kullanmayı sağlar.
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:
// Doğru olan yöntem
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.