2015 yılının Advent of Code etkinliği, programlama yeteneklerini test etmek isteyen birçok geliştirici için unutulmaz bir deneyim olmuştur. Ben de bu serüvene Clojure diliyle katıldım ve ilk sekiz günün ardından, hem çözümlerimdeki basitlikten hem de karşılaştığım zorluklardan pek çok şey öğrendim. Şimdi, bu günlerde nasıl ilerlediğime dair bir özet paylaşacağım—özellikle, Clojure'in fonksiyonel yapısının sorunların üstesinden gelmede ne kadar etkili olduğunu keşfettiğim anları da ekleyeceğim.
Yönlendirme ve Konum İzleme: 3. Günün Basitliği
Üçüncü günün problemi, verilen bir dizi yönlendirme komutuna (Kuzey/Güney/Doğu/Batı) göre kaç farklı noktayı ziyaret ettiğimizi bulmaktı. Bu tür problemlerde genellikle konum değişikliklerini bir harita olarak saklamak işleri kolaylaştırır. Ben de Clojure’de yönlendirme komutlarını koordinat değişimlerine dönüştürmek için aşağıdaki gibi basit bir tanımlama yaptım:
(def moves {\^ [0 1] ; Kuzey: y koordinatını 1 artır
\v [0 -1] ; Güney: y koordinatını 1 azalt
\> [1 0] ; Doğu: x koordinatını 1 artır
\< [-1 0] ; Batı: x koordinatını 1 azalt})Ardından, sadece bu haritayı kullanarak her adımda konumumu güncelledim ve ziyaret edilen tüm noktaları bir sete ekledim. Clojure’in immutable veri yapıları burada çok işe yaradı—her adımda yeni bir konum vektörü oluşturmak, durum yönetimini basit ve hata yapmaya karşı dirençli hale getirdi.
Kripto Para Madenciliği: 4. Günün Yoğun Hesaplamaları
Dördüncü gün, bir çeşit kripto para biriminin madenciliğiyle ilgiliydi. Amacımız, verilen bir sayıdan başlayarak, MD5 hash’inin belirli sayıda sıfırla başlamasını sağlayan en küçük sayıyı bulmaktı. Java interoperabilitesini kullanarak, Clojure içinde kolayca erişebildiğim java.security.MessageDigest sınıfını kullandım:
(let [digest (.digest (doto (java.security.MessageDigest/getInstance "MD5")
(.update (.getBytes (str number)))))]
(Integer/parseInt (apply str (take 5 (map #(format "%02x" %) digest))) 16))Bu yaklaşım oldukça brute-force’tu ve beklediğim gibi yavaş çalıştı. Clojure’in paralel hesaplama yeteneklerini kullanarak performansı artırmak mümkün olsa da, ben sadece hesaplamanın bitmesini beklemeyi tercih ettim. Kripto dünyasına adım atmak için henüz erken olduğuna karar verdim!
Metin Analizi: 5. ve 8. Günlerdeki Zorluklar
Beşinci ve sekizinci günler, temelde metin işleme problemleriydi. Beşinci günde, bir kelimenin "güzel" olup olmadığını belirlemek için üç koşuldan geçmesini gerektiren bir fonksiyon yazmam gerekti:
- En az üç ünlü harf içermeli
- En az bir harf ardışık olarak iki kez tekrarlanmalı
- Belirli yasaklı dizilerden hiçbirini içermemeli
Clojure’in fonksiyonel özellikleri burada da devreye girdi; fonksiyonu aşağıdaki gibi tanımladım:
defn nice?
"Belirli kurallara göre kelimeyi 'güzel' olarak değerlendirir"
[word]
(and (has-three-vowels? word)
(has-repeated-letter? word)
(no-forbidden-strings? word)))Sekizinci günde ise, bir string’in bellekteki gerçek boyutu ile kodlanmış hali arasındaki farkı hesaplamamız gerekiyordu. Bu, Clojure’in string manipülasyonu için sunduğu araçlarla oldukça basit bir şekilde çözülebildi.
Işıkların Yönetimi: 6. Günün Matris Problemleri
Altıncı gün, verilen bir 1000x1000 ışık matrisinde talimatlara göre ışıkları açma, kapama ve değiştirme işlemlerini gerçekleştirmekti. Talimatlar metin formatında verilmişti ve her biri bir dikdörtgen alan tanımlıyordu. Clojure’in vektörler ve fonksiyonlar arası geçiş kolaylığı sayesinde, bu problemi de oldukça okunabilir bir şekilde çözdüm. Örneğin, bir ışık matrisini tanımlayıp talimatları uygulamak şu şekildeydi:
def initial-grid (vec (repeat 1000 (vec (repeat 1000 0))))
def apply-instruction [grid [action x1 y1 x2 y2]]
(reduce (fn [g [x y]]
(update-in g [x y] (case action
"turn on" inc
"turn off" dec
"toggle" #(+ 2 %))))
grid
(for [x (range x1 (inc x2))
y (range y1 (inc y2))]
[x y]))Mantık Devreleri: 7. Günün Grafiksel Yaklaşımı
Yedinci gün, basit bir mantık devresini simüle etme problemiydi. Verilen bir dizi mantık kapısı ve bağlantılar, sonuçta belirli bir düğümde oluşacak değeri hesaplamamızı gerektiriyordu. En basit yaklaşım olarak, tüm talimatları sırayla uyguladım ve her seferinde hangi düğümlerin hesaplanabileceğini kontrol ettim. Örneğin, aşağıdaki gibi bir girdi verildiğinde:
123 -> x
456 -> y
x AND y -> d
x OR y -> e
x LSHIFT 2 -> f
y RSHIFT 2 -> g
NOT x -> h
NOT y -> iHer seferinde, henüz hesaplanmamış düğümleri atlayarak talimatları tekrar tekrar işledim. Bu brute-force yöntemi, problemi çözmek için yeterliydi, ancak daha optimize bir yaklaşım, düğümler arasındaki bağımlılıkları bir grafik olarak modelleyip topolojik sıralama kullanmak olabilirdi.
Girdi İşleme: Verilerin Düzenlenmesi
Tüm bu problemlerde en ilginç gözlem, girdilerin nasıl işlendiğiydi. Örneğin, ikinci günde veriler 1x2x3 formatında iken, sekizinci günde London to Dublin = 464 gibi metin tabanlı veriler kullanılıyordu. Bu durumlarda, girdiyi parse etmek için Clojure’in hem string manipülasyonu hem de regex yeteneklerini kullanmak gerekiyordu. Ancak, daha temiz bir yaklaşım olarak, aşağıdaki gibi bir yardımcı fonksiyon tanımlamak mümkündü:
defn parse-input
"Belirtilen formata göre girdiyi parse eder"
[file pattern]
(let [lines (clojure.string/split-lines (slurp file))
re (re-pattern (clojure.string/replace pattern #"<(\w+):(\w+)>" "(?<$1>$2)"))]
(map #(let [[_ & groups] (re-matches re %)]
(zipmap (map keyword (re-seq #"<\w+:" pattern))
(map read-string groups)))
lines)))Bu fonksiyon, hem statik hem de dinamik girdi formatlarını destekleyebilir ve Clojure’in fonksiyonel özellikleriyle birleştirildiğinde oldukça esnek bir yapı sunar.
Gelecekte, Advent of Code gibi etkinliklerde karşılaşılan problemlerin karmaşıklığı arttıkça, hem algoritmik düşünce hem de fonksiyonel programlama dillerinin sunduğu avantajlar daha da belirgin hale gelecektir. Clojure’in immutable veri yapıları ve fonksiyon kompozisyon yetenekleri, özellikle bu tür zorlu problemlerde geliştiricilere büyük bir avantaj sağlıyor. Eğer siz de bu tarz algoritmik yarışmalara katılmayı düşünüyorsanız, fonksiyonel dillerin sunduğu araçları keşfetmek iyi bir başlangıç olabilir.
Yapay zeka özeti
Clojure kullanarak 2015 Advent of Code'un 3 ila 8. günlerini nasıl çözdüğünüzü öğrenin. Fonksiyonel programlama ve algoritmik yaklaşımların gücünü keşfedin.