7 Haziran 2012 Perşembe

Haskell'de veri tipleri

        Daha öncede bahsettiğim gibi Haskell strongly typed bir dil olduğundan, veri tipleri hakkında bilgi sahibi olmak en önemli şeylerden biridir. Daha önceki Haskell kodlarında farkettiyseniz, değişken kullanırken veya fonksiyon yazarken tiplerini belirtmeden yazmıştık. Bunun nedeni Haskell'in tip çıkarsama(type inferencing) yapan bir dil olmasıdır. Tip çıkarsama yapan dillerde, dil sizin değişkeni kullanışınıza göre değişkene bir tip ataması yapar. Haskell statik tip bir dildir, yani tip ataması derleyici tarafından derleme zamanında gerçekleşir. Şimdi Haskell'deki yapısal(build-in) veri tiplerine şöyle bir bakalım:

İsimAçıklama
Int Tamsayı-232-1 ile 232-1 arasındaki değerleri tutabilir.
IntegerTamsayıInt kadar verimli değildir. Fakat sınırsız uzunlukta sayı tutabilir.
FloatOndalık sayıVirgülden sonraki kısımın kesinlik azdır
DoubleOndalık sayıVirgülgen sonrası için kesinlik değeri Float'ın 2 katıdır.
BoolMantıksalSadece doğru(True) veya yanlış(False) değerlerini tutabilir.
CharKarakterTek bir karakter tutabilir.

      Daha önce derleyici olarak ghc kullandığımı söylemiştim, ghc'nin bir güzel yanıda kendisine ait bir yorumlayıcısı olmasıdır. ghc'nin yorumlayıcısını kullanmak için linuxta komut satırına 'ghci' yazmanız yeterli olacaktır. Son olarak aşşağıdaki kodu anlamak için Haskell'de 'let' kelimesinin yorumlayıcı üzerinde geçici olarak değişken tanımlamanızı sağladığını bilmeniz şimdilik yeterlidir.  


Yukarıdaki kodda ghc'nin yorumlayıcısını kullanarak Haskell'deki yapısal tipleri göstermeye çalıştım. x değişkenine farklı değerler atadım daha sonrada ':t' komutuyla değişkene yorumlayıcının otomatik olarak atadığı tipi gösterdim. İlk satırda farkedeceğiniz üzere ::Int diye bir terim kullandım. Bu terim x değişkenine yorumlayıcının değilde benim istediğim tipin atanmasını sağlar, yani bir casting işlemi yapar. Peki ya casting yapmasaydım? Bu durumda 4. ve 6. satırlarda görebileceğiniz gibi yorumlayıcı otomatik olarak kendi istediği tipi yani Integer atayacaktı. Aynı şeyin Double ve Float tiplerindede olduğnu görebilirsiniz. Son iki 'let' komutuna bakarsak karakter ile string arasındaki farkıda görebiliriz. Son 'let' komutunun bize açıkca belirttiği şey; stringler aslında karakterlerden oluşan listelerden başka bişeyler değillerdir(Köşeli parantezler liste operatörleridir.). Listeleri daha ilerde inceleyeceğiz.

Aşşağıdaki Haskell kodunu ve çıktısını inceleyelim:

tip.hs
testFloat::Integer->Float
testFloat n = (fromInteger (toInteger n)) * 2.12345678911112

testDouble::Int->Double
testDouble n = (fromInteger (toInteger n)) * 2.12345678911112

main = do{    putStrLn "testFloat => ";
            print(testFloat 12);
            putStrLn "testDouble => ";
            print(testDouble 12);
        }


        Değişkenler üzerinde casting yaparak istediğimiz tipi atayabiliyoruz, peki ya fonksiyonlarda? Fonksiyonlardada bir fonksiyonun parametre olarak içine ne alacağını ve değerinin ne olacağını kendimiz belirtebiliriz. Yukarıdaki kodda  testFloat adlı fonksiyonun başına aşşagıdaki kodu koymasaydım fonksiyonun döndüğü değer derleyicinin otomatik atadığı değer yani Double olurdu.
testFloat::Integer->Float
Kod açıkça şunu ifade eder; testFloat parametre olarak Integer tip alır ve değeri Float bir tiptir. Bu ifadeyi daha iyi anlamak için hemen matematiksel fonksiyonları düşünelim. Biz matematiksel bir fonksiyon tanımlarken aslında yukarıdaki ifadeyi kullanırız:
f: \mathbb{Z} \!\, => \mathbb{R} \!\,
f(x) = x+π
Gördüğünüz gibi fonksiyonu tanımlarken tamsayılar kümesinden reel sayılar kümesine gittiğini gösterdik. Burda aslında x'in bir tamsayı, fonksiyonun değerininde bir reel sayı olduğunu söyledik. Aynı yukarıda kodumuzda, testFloat fonksiyonunun  Integer tipindeki sayılar kümesinden, Float tipinde sayılar kümesine gittiğini söylediğimiz gibi. Son olarak birden fazla parametre alan fonksiyonlarda yukarıdaki işin nasıl olduğuna bir bakalım:

us.hs
pow::Float->Float->Float
pow a b = a**b

main = print(pow 2 2)
Ilk satır biraz önceki gibi bir mantığa sahip değilmiş gibi gözükebilir ama aslında aynı mantık. pow fonksiyonunun söyle bir fonksiyon olduğunu düşünelim pow 2 x bu durumda ilk satırımızda şöyle olurdu pow::Float->Float. Yani Float tipinde bir parametre alıyor ve fonksiyonumuz Float tipinde. Peki fonksiyonumuz normalken: pow x y. Ozaman ilk satırımız pow::Float->(Float->Float) olur değilmi. Yani fonksiyonumuz Float tipinde bir değer alıp Float tipinden Float tipine giden bir fonksiyona dönüşüyor. Şöyle bir toparlamak gerekirse:
(pow 2 2) Float

(pow 2)(x) Float ->Float

(pow (y))(x) Float->(Float->Float)
Burada şöyle düşünülebilinir; birden fazla parametre alan bir fonksiyona parametrelerinden sadece birini verirsek aslında yeni bir fonksiyon elde ederiz. Bu kısım biraz karışık, ilerleyen zamanlarda yeniden inceleyebiliriz.

Hiç yorum yok:

Yorum Gönder