Arrayler ve slicelar
Bu bölümün bütün kodlarını burada bulabilirsiniz
Arrayler aynı tipte birden çok elementi bir değişken içerisinde belirli bir sırada saklamanızı sağlar.
Arrayler üzerinde iterasyon yapmanız çok yaygındır.Hadi, Sum
fonksiyonunu yapmak için yeni öğrendiğimiz for
'u kullanalım. Sum
sayıların olduğu bir array alacak ve toplamı dönecek.
Hadi, TDD yeteneklerimizi kullanalım.
İlk olarak test yaz
Çalışmak için yeni bir klasör oluşturun.sum_test.go
isminde bir dosya oluşturun ve aşağıdakileri ekleyin:
Arraylar sabit bir kapasiteye sahiptirler, bir array değişkenini tanımlarken kapasitesini de belirtiriz. Bir arrayi iki yolla oluşturabiliriz:
[N]type{value1, value2, ..., valueN} e.g.
numbers := [5]int{1, 2, 3, 4, 5}
[...]type{value1, value2, ..., valueN} e.g.
numbers := [...]int{1, 2, 3, 4, 5}
Bazen hata mesajlarında girdileri yazdırmak faydalıdır. Burada arraylerde de iyi çalışan %v
fotmatlayıcısını değerleri "varsayılan" formatta yazdırması için kullandık.
String formatları hakkında daha fazla bilgi edinin
Testi çalıştırmayı dene
go test
komutunu çalıştırdığınızda derleyici ./sum_test.go:10:15: undefined: Sum
hatasını verecek.
Testin çalışması için minimum kodu yaz ve başarısız test çıktılarını kontrol et
sum.go
içerisine
Şimdi testiniz temiz bir hata mesajı verecek.
sum_test.go:13: got 0 want 15 given, [1 2 3 4 5]
Testi geçecek kadar kod yaz
Bir arrayin belirli bir indeksindeki değere ulaşmak için array[index]
sözdizimini kullanın. Bu durumda, for
kullanarak array üzerinde 5 kez iterasyon yaparak array içerisindeki her değeri sum
değişkenine ekliyoruz.
Refactor
Kodumuzu temizlemeye yardımcı olması için range
anahtar kelimesini tanıtalım
range
array üzerinde iterasyon yapmanızı sağlar. range
, her iterasyonda indeksı ve indeksin değerini döner. (Blank Identifier) _
karakterini kullanarak indeks değerini yok sayabiliriz.
Arrayler ve tipleri
Arraylarin ilginç bir özelliği, boyutun kendi tipinde kodlanmış olmasıdır. Eğer [4]int
arrayini [5]int
bekleyen bir fonksiyona gönderirseniz, kod derlenmeyecektir. Bunlar farklı tiplerdir bu işlem aynı int
bekleyen bir fonksiyona string
göndermek gibidir.
Arraylarin sabit bir uzunluğa sahip olmasının oldukça zahmetli olduğunu düşünüyor olabilirsiniz ve muhtemelen çoğu zaman onları kullanmayacaksınız!
Go, koleksiyonunun boyutunu önemsemediği hatta herhangi bir boyutta olabilecek olan slice tipine sahiptir.
Bir sonraki koşul, farklı boyutlardaki koleksiyonları toplamak olacak.
İlk olarak test yaz
Herhangi bir boyutta koleksiyonlara sahip olmamızı sağlayan slice tipini kullancağız. Sözdizimi arraylere çok benziyor sadece değişkeni tanımlarken boyutunu kaldırın.
myArray := [3]int{1,2,3}
yerine mySlice := []int{1,2,3}
Dene ve testi çalıştır
Bu derlenmeyecektir
./sum_test.go:22:13: cannot use numbers (type []int) as type [5]int in argument to Sum
Testin çalışması için minimum kodu yaz ve başarısız test çıktılarını kontrol et
Bizimde yapabileceğimiz, buradaki problem
Sum
ın argümanını arrayden slicelara alarak var olan API'yı bozmak. Bunu yaptığımızda muhtemelen başkasının gününü mahfedeceğiz çünkü diğer testimiz derlenmeyecek!Yeni bir fonksiyon oluşturmak
Bizim durumumuzda, fonksiyonumuzu başka hiç kimse kullanmıyor, bu yüzden bakımı yapılacak iki fonksiyona sahip olmak yerine, sadece bir fonksiyona sahip olalım.
Eğer testleri çalıştırmayı denerseniz derlenmeyecektir, ilk testin çalışabilmesi için array yerine slice'a çevirmelisiniz.
Testi geçecek kadar kod yaz
Burada yapmamız gereken tek şey derleyici hatalarını gidermek ve testlerin geçmesini sağlamak!
Refactor
Sum
fonksiyonunu önceden düzenlemiştik - Tek yaptığımız arrayler yerine slicelar yazmak, ekstra bir değişikliğe gerek yok. Yeniden düzenleme aşamasında test kodumuzu ihmal etmememiz gerektiğini unutmayın - Sum
testlerimizi daha da geliştirebiliriz.
Testlerimizin değerini sorgulamak önemlidir. Mümkün olduğu kadar çok test olması bir amaç olmamalı ancak kod tabınında (code base) mümkün olduğu kadar tatminkarlık olmalı. Çok fazla testin olması gerçek bir probleme dönüşebilir ve bakımı için ekstra yük olabilir. Her testin bir maliyeti vardır.
Bizim durumumuzda, bu fonksiyon için iki test olması gereksiz. Eğer bir slice için belirli bir boyutta çalışıyorsa (makul ölçüde) her boyuttaki slice için çalışacaktır.
Go'nun dahili test aracı kapsama aracı (coverage tool) özelliğini içerir. %100 kapsama için çabalamak nihai hedefiniz olmamalıdır, kapsama aracı kodunuzun testler tarafından kapsanmayan alanlarını belirlemenizde size yardımcı olabilir. TDD konusunda katıysanız, 100%'e yakın kapsama oranına sahip olmanız çok olasıdır.
Çalıştırmayı deneyin
go test -cover
Görmelisiniz
Şimdi testlerden birini silin ve kapsama oranını bir daha kontrol edin.
Artık iyi test edilmiş bir fonksiyonumuz olduğu için mutluyuz sonraki mücadeleye girmeden önce yapmış olduğunuz harika işi teslim (commit) etmelisiniz.
SumAll
isminde yeni bir fonksiyona ihtiyacımız var. Bu fonksiyon çeşitli sayıda sliceı parametre olarak alacak, yeni bir slice dönecek ve bu slice her sliceın toplamını içerecek.
Örneğin
SumAll([]int{1,2}, []int{0,9})
fonksiyonu []int{3, 9}
döndürmeli
veya
SumAll([]int{1,1,1})
fonskiyonu []int{3}
döndürmeli
İlk olarak test yaz
Dene ve testi çalıştır
./sum_test.go:23:9: undefined: SumAll
Testin çalışması için minimum kodu yaz ve başarısız test çıktılarını kontrol et
Bizim testimize göre SumAll
fonksiyonunu tanımlamalıyız.
Go çeşitli sayıda argüman alan (variadic functions) yazmanıza izin verir.
Bu geçerlidir ancak testlerimiz hala derlenmeyecektir!
./sum_test.go:26:9: invalid operation: got != want (slice can only be compared to nil)
Go slicelar ile eşittir operatörünü kullanmanıza izin vermez. Her got
ve want
slicelarını iterate etmesi için bir fonksiyon yazabilirsiniz ancak kolaylık olması için, herhangi iki değişkenin aynı olup olmadığını görmemizde kullanışlı olan, reflect.DeepEqual
kullanabiliriz.
(DeepEqual
fonksiyonuna erişebilmek için dosyanın en yukarısında import reflect
ekli olduğundan emin olun)
Bunu belirtmek önemli, reflect.DeepEqual
"type safe" güvenli tipte değildir - saçma bir şey yapsanızda kodunuz derlenecektir. Uygulamada bunu görmek için geçici olarak testi değiştirin:
Burada yaptığımız şey bir slice
ile bir string
'ı kıyaslamak. Bu kıyaslamayı yapmak mantıklı değil ancak test derleniyor! reflect.DeepEqual
slicelar ile diğer şeyleri kıyaslamak için kullanışlı, kullanırken dikkatli olmalısınız.
Testi eski haline getirin ve çalıştırın. Aşağıdakine benzer bir test çıktısı elde etmelisiniz
sum_test.go:30: got [] want [3 9]
Testi geçecek kadar kod yaz
Burada yapmamız gerekn varargs üzerinde iterate etmek, Sum
fonksiyonu ile toplamı hesaplamak, sonucu döneceğimiz olan slice'a eklemek
Öğrenilecek birçok yeni şey!
Slice oluşturmak için yeni bir yol. make
, üzerinde çalışacağımız numbersToSum
'ın len
ile elde ettiğimiz uzunluğunu kullanarak slice'ın kapasitesini belirlememizi sağlar.
Slicelar da arrayler gibi mySlice[N]
indeksteki değere erişebilir veya =
ile yeni bir değer atayabilirsiniz.
Testler şimdi geçmeli.
Refactor
Daha önce bahsettiğimiz gibi, sliceların kapasitesi vardır. Eğer slicesınızın boyutu 2 ve mySlice[10] = 1
işlemini yapmaya çalışırsanız çalışma sırasında (runtime) hatası alırsınız.
Bunun yanı sıra, append
fonksiyonuna slice ve yeni bir değer göndererek yeni bir slice elde edebilirsiniz.
Bu uyarlamada, kapasite hakkında daha az endişeliyiz. Boş bir slice olan sums
ile başlıyoruz ve Sum
fonksiyonunun sonucunu ekliyoruz.
Sıradaki koşulumuz SumAll
fonksiyonunu SumAllTails
'a çevirmek, bu sayede her sliceın "tails" değerini hesaplayacağız. Tail (kuyruk), koleksiyonun ilk değeri ("kafa") haric tüm değerlerdir.
İlk olarak test yaz
Dene ve testi çalıştır
./sum_test.go:26:9: undefined: SumAllTails
Testin çalışması için minimum kodu yaz ve başarısız test çıktılarını kontrol et
FonksiyonuSumAllTails
olarak yeniden isimlendir ve testi tekrar çalıştır
sum_test.go:30: got [3 9] want [2 9]
Testi geçecek kadar kod yaz
Slicelar dilimlenebilir! Sözdizimi slice[low:high]
. Eğer :
bir tarafında ki değeri kaldırırsanız o taraf haric her değeri kapsar. Bizin durumumuzda, numbers[1:]
ile "1. indeksten sona kadar" almasını söylüyoruz. Slicelar hakkında başka testler yazmak için biraz zaman harcamak ve daha aşina olmak için slice operatörüyle denemeler yapmak isteyebilirsiniz.
Refactor
Bu sefer refactor için fazla bir şey yok.
Fonksiyonumuza boş bir slice gönderdiğimizde ne olacağını düşündünüz mü? Boş bir slice'ın "tail" (kuyruğu) nedir? Go'ya boş bir slice'ın myEmptySlice[1:]
bütün elemanlarını almasını söylerseniz ne olur?
İlk olarak test yaz
Dene ve testi çalıştır
O hayır! Test derlendi ancak çalışma sırasında (runtime) hata verdi. Derleme zamanı hataları bizim dostumuzdur çünkü çalışan yazılımlar yapmamıza yardım ediyorlar, çalışma zamanı hatalar düşmanımız çünkü kullanıcıları etkiliyorlar.
Testi geçecek kadar kod yaz
Refactor
Testimizde assertion etrafında yine tekrarlı kodlar var, öyleyse bunları bir fonksiyona çıkaralım.
Bunun kullanışlı bir yan etkisi, kodumuza biraz tip güvenliği eklemesidir. Eğer geliştirici kazara yeni bir test eklerse checkSums(t, got, "dave")
derleyici onların çalışmasını durduracak.
Özetlersek
Ele alınanlar
Arrayler
Slicelar
Slice oluşturmanın çeşitli yolları
sabit kapasiteli oldukları ama
append
kullanarak nasıl yenisini oluşturulacağıSliceları nasıl dilimleyeceğini!
len
ile array veya slice'ın uzunluğunu elde etmeyiTest kapsama aracını (coverage tool)
reflect.DeepEqual
kullanmanın neden kullanışlı olduğu ama kodumuzun tip güvenliğini (type-safety) düşürebileceği
Sliceları ve arraylari integerlar ile kullandık ancak diğer tiplerle de çalışabilirler, arrayler/sliceların kendileri de dahil. Eğer ihtiyacınız varsa [][]string
ile tanımlayabilirsiniz.
Slicelar hakkında derinlemesine bir bakış için Go blog'una bakabilirsiniz. Okuyarak öğrendiklerinizi pekiştirmek için daha fazla test yazmayı deneyin.
Test yazmaktan başka Go ile deneme yapmanın bir başka kullanışlı yolu da Go playgrounddır. Soru sormanız gerekirse kodunuzu kolayca paylaşabilirsiniz ve çoğu şeyi deneyebilirsiniz. Slice ile deneyler yapabilmeniz için Go playground'u hazırladım.
Array'in dilimlenmesi ve slice'ın değiştirilmesinin orjinal diziye nasıl etkilediğinin örneği; "Kopya" slice orjinal arraye etki etmeyecektir. Neden büyük bir slice'ı dilimledikten sonra slice'ın kopyasını almak neden iyi bir fikirdir başka bir örnek.
Bu sayfa @bariscanyilmaz tarafından çevrildi.
Last updated