Tonguç Yumruk'un Weblog'u
Anlık olaylar, fikirler, gudik ismail vs...

Sun, 10 Aug 2008

Bence Common Lisp versiyonundan daha güzel.

module Main where

import System.Time

tdiff d = do now <- getClockTime
             return $ diffClockTimes d now

safak = toClockTime (CalendarTime 2008 September 16 0 0 0 0 Tuesday 0 "" 0 True)

main = tdiff safak >>= return . (timeDiffToString . normalizeTimeDiff) >>= print
tonguc@terra:~% runhaskell Main.hs
"1 month, 6 days, 6 hours, 16 mins, 20 secs"
tonguc@terra:~%
[20:46] | [] | # | G! |

Wed, 05 Sep 2007

Birkaç yıldır mütemadiyen fonksiyonel programlama ile ilgileniyorum. Ancak bir süre öncesine kadar fonksiyonel programlamanın sunduğu asıl güç olan fonksiyonları alıp çeşitli şekillerde yeniden düzenleme konusuna pek hakim olamadığımı, o teknikleri çok fazla kullanamadığımı hissediyordum.

Önceleri bunun genelde ilgilendiğim konuların fonksiyonel programlamaya çok uygun olmamasından kaynaklandığını düşündüysem de zaman geçip bu alanda uğraşmaya devam ettikçe sorunun bu olmadığının farkına vardım. Sorunun kaynağı yazdığım fonksiyonların diğer fonksiyonlar ile düzgün bir biçimde birleştirilmeye pek müsait olmamasıydı. Bundan bir yıl önce yazdığım kodlara bakıyorum ve tam bir kontrol yapısı karmaşası görüyorum. İç içe geçmiş for/if yapıları vs... Daha önceden de sürekli duyup, bilip uygulamadığım bir yöntemin önemini bunn üzerine kavramış oldum. Bir fonksiyonun içerisinde, asla birden fazla kontrol/döngü yapısı bulunmaması gerekir

Eğer fonksiyonlarınız içerisinde birden fazla kontrol yapısı barındırmıyorsa (ve tabiiki fonksiyonel programlamanın soğasına uygun olarak state bağımsız ise) bu fonksiyonlar diğer fonksiyonlar ile birleştirilerek yeni fonksiyonlar ortaya çıkarabilirsiniz. Eğer programlama diliniz de bu tip işleri kolaylaştıran Haskell gibi bir dil ise şu gibi ilginç örnekler ortaya çıkabilir:

group :: Eq a => [a] -> [[a]]
group = (flip group') []

group' :: Eq a => [a] -> [[a]] -> [[a]]
group' [] y = y
group' (x:xs) [] = group' xs [[x]]
group' (x:xs) (z@(y:ys):zs) | x == y = group' xs $ (x:z):zs
                            | otherwise = group' xs $ [x]:(z:zs)

comparing :: Ord b => (a -> b) -> a -> a -> Ordering
comparing p x y = compare (p x) (p y)

maximumBy :: Ord a => (a -> a -> Ordering) -> [a] -> a
maximumBy f (x:[]) = x
maximumBy f (x:xs) = case (f x y) of
                       LT -> y
                       EQ -> x
                       GT -> x
    where
      y = maximumBy f xs

mostCommon :: Ord a => [a] -> a
mostCommon = head . maximumBy (comparing length) . group . sort

Tabii yukarıdaki kod banim icadım değil, hatırlamadığım bir blogdan alıntı. Bu ve bunun kadar fantastik birçok kod örneği hergün Planet Haskell'de yayınlanıyor.

Burada dikkat çeken noktalar ne? Öncelikle şunu aklımızın bir köşesine koyalım. "." karakteri fonksiyon bileşke operatörüdür. Örneğin:
group . sort = group(sort(x))

Yeri gelmişken bahsedilmeyi hak eden bir diğer güzel özellik ise "partial application". Bu ne demek? Partial application, fonksiyonların otomatik olarak "currying" işleminden geçmesidir. Bu sayede "+ 20" yazmamız + fonksiyonuna tek parametre için currying uygulamamız anlamına geliyor. Açıklamak gerekirse, + diğer birçok fonksiyondan hiçbir farkı olmayan bir fonksiyon. Bu fonksiyon iki parametre alıyor (ismi a ve b olsun). Biz bu fonksiyona parametrelerinden sadece birini (a'yı) verecek olursak elde edeceğimiz şey tek parametre (b) alan yeni bir fonksiyondur. Daha sonra bu fonksiyonu kullanarak istediğimiz herhangi bir b sayısını a ile toplamamız mümkün olur. İşte partial application denilen nimet, currying adlı işi otomatik olarak uygular ve bize bu fonksiyonu üretir.

Bugün yazdığım ikinci süper dağınık blog girdisi ile verdiğimiz rahatsızlıktan ötürü özür dileriz.

[14:41] | [] | # | G! |

Thu, 26 Apr 2007

Çünkü şöyle ilk bakışta hiyeroglif gibi gözüken bir kod yazarak tüm fibonacci sayılarını içeren sonsuz bir liste oluşturmak mümkün. Tabii bir de bu listenin sıradan bir fibonacci implementasyonundan kat kat hızlı hesaplanması durumu var...

fib = 1 : 1 : [x + y | (x,y) <- zip fib (tail fib)]

Daha fazlası için anahtar kelimeler:

[23:20] | [] | # | G! |

Sun, 17 Dec 2006

En son CL ile uğraştığımızda sembol şeklinde aldığımız IP adresini listeye çevirmiştik. Peki ya bu listeyi yeniden bir string haline çevirmek istersek? İşte, format ile tanışma zamanı.

Diğer birçok dildeki %s, %d gibi basit metin biçimleme ifadelerinin aksine Common Lisp'in format komutu sadece bir metin biçimlendiriciden çok daha ötesidir. format, kendi içinde koşul ve döngü yapıları da barındıran ufak ve kriptik bir programlama dilidir. En basitinden bir format ifadesi şöyle olabilir:

CL-USER> (format nil "~a" 42)
"42"
CL-USER>

format dilinde operatörler "~" ön eki ile başlarlar. "~" işareti ile tek karakterden oluşan operatör arasında ise o operatörün aldığı parametreler bulunur. Bir örnek vermek gerekirse:

CL-USER> (format nil "~$" pi)
"3.14"
CL-USER> (format nil "~5$" pi) ;bu sefer noktadan sonra 5 basamak yaz
"3.14159"
CL-USER>

Burada uzun uzadıya format anlatacak değilim, zira kendisi oldukça uzun ve detaylı özelliklere sahip. format hakkında daha detaylı bilgi almak için Practical Common Lisp'in buna ayırdığı bölümü okuyabilirisiniz. Biz elimizdeki işe dönelim. Elimizde liste şeklinde ifade edilen bir IP adresi var, ve bunu bir metine çevirmemiz lazım. Bu iş için format bize ilginç bir operatör sunar: "~{ <ifade> ~}". Bu operatör, parametre olarak verilen liste içerisindeki her eleman için içeride verilen ifadeyi çalıştırır. Bunu bizim IP adresi örneğimize uygularsak:

CL-USER> (format nil "~{~d.~}" '(192 168 1 1))
"192.168.1.1."
CL-USER>

Güzel, hoş ama o sondaki nokta da ne öyle? Her sayının sonuna nokta koyarsak olacağı buydu. Peki listenin son elemanından sonra nokta koymaması için ne yapabiliriz? Bu iş için format bize bir başka ilginç araç sunar: "~^". Bu operatör sayesinde format işleminin bu operatörden sonraki kısmı listenin son elemanına uygulanmaz. Deneyelim, ve görelim:

CL-USER> (format nil "~{~d~^.~}" '(192 168 1 1))
"192.168.1.1"
CL-USER>

Evet, bu kesinlikle daha güzel oldu. Format ile daha birçok ilginç şey yapmanız ve şaşırmanız mümkün. İşte bunlardan birkaçı. Mesela ya kullanıcılarınız rakamları tanıyamıyor, fakat İngilizce okuyunca anlayabiliyorsa?

CL-USER> (format nil "~{~r~^ dot ~}" '(192 168 1 1))
"one hundred ninety-two dot one hundred sixty-eight dot one dot one"
CL-USER> (format nil "~{~r~^ nokta ~}" '(192 168 1 1))
"one hundred ninety-two nokta one hundred sixty-eight nokta one nokta one"
CL-USER>

Peki ya kullanıcılarınız zamanda yolculuk yaparak Eski Roma'dan gelmişse?

CL-USER> (format nil "~{~@r~^ . ~}" '(192 168 1 1))
"CXCII . CLXVIII . I . I"
CL-USER>

Eski Roma'lı zaman yolcuları için yazılım üretmenin ne kadar mantıklı bir iş olduğunu tartışmaya başlamadan önce bir milyon tane tuhaf özelliği içerisinde barındıran yazılımlarla ilgili olarak Joel Spolsky'nin dediklerini bir okumakta fayda var.

[16:05] | [] | # | G! |

Wed, 15 Nov 2006

Dün Common Lisp ve ağ uygulamaları gibi şeylerle oynarken kullanıcıdan bir IP adresine ilişkin bilginin en kolay nasıl alınabileceğini düşündüm. Tabii interaktif bir uygulamada bu o kadar büyük bir problem değil. Peki ya CL kodu şeklinde bir programın ayar dosyasını üretmeye çalışıyorsak? O zaman kullanıcının bu değerleri çift tırmak karakterleri içinde yazması gerekirdi ki bu da eğlenceli değil. Bunun üzerine ilk aklıma gelen şey kullanıcıların #IP192.168.1.1 şeklinde yazabilecekleri bir Reader Macro yazmak olduysa da sonradan daha kolay bir yöntem olduğunu farkederek aydınlandım! Common Lisp'te en çok uğraştığımız şeylerden biri nedir? Semboller! Peki bir IP adresi sembol olabilir miydi? En kolay yolu denemekti...

; SLIME 2005-12-27
CL-USER> '192.168.1.1
|192.168.1.1|
CL-USER>

Evet, IP adreslerini sembol olarak kullanabiliyoruz... Güzel. Peki bu sembolü kendi programımızın kullandığı IP adresi biçimine nasıl çeviririz? Hm... Mesela bir metine çevirip metni de "." karakterini kullanarak bölsek... Belki... Önce bir sembolümüzü metne çevirelim...

CL-USER> (symbol-name '192.168.1.1)
"192.168.1.1"
CL-USER>

Tamam, bunu yapabiliyorsak geriye kaldı metni işlemek... İşimizi kolaylaştırması için split-sequence paketini kullanmamız hiç fena olmaz bence...

CL-USER> (asdf:oos 'asdf:load-op :split-sequence)
; loading system definition from
; /usr/share/common-lisp/systems/split-sequence.asd into #<PACKAGE "ASDF0">
; registering #<SYSTEM :SPLIT-SEQUENCE {B015E11}> as SPLIT-SEQUENCE
NIL
CL-USER> (split-sequence:split-sequence #\. (symbol-name '192.168.1.1))
("192" "168" "1" "1")
11
CL-USER>

Tamam, bir yerlere gelmeye başladık. Fakat aklıbaşında hiçbir insan evladının bir IP adresini dört adet metin halinde saklayacağını sanmıyorum. En iyisi biz bunları sayı yapalım.

CL-USER> (mapcar #'parse-integer (split-sequence:split-sequence #\. (symbol-name '192.168.1.1)))
(192 168 1 1)
CL-USER>

Voila!

Not: Tabii bu iş Python'la map(int, "192.168.1.1".split('.')) şeklinde de yapılabilir. Ancak ne yazık ki Python'da sembol diye bir yapı olmadığı için metin kullanmak zorunda kalıyoruz ki, bu da zaten asıl amaca ters. Python ile ne kadar DSL yazılır, dil ne derece eğilip bükülebilir konularına ise girmek istemiyorum...

[15:26] | [] | # | G! |
Elektrikli Posta
Uptime
7 ay, 3 gün, 18 saat, 51 dakika
Çocuklara Bilgisayar
Kategoriler
/ (181)
  dev/ (11)
  kod/ (24)
  linux/ (37)
    freedays/ (2)
    senlik/ (0)
      2006/ (15)
  misc/ (52)
  mobil/ (3)
  musiki/ (16)
  net/ (9)
  tech/ (7)
  web/ (5)
Zaman Makinesi
< August 2008 >
SuMoTuWeThFrSa
      1 2
3 4 5 6 7 8 9
10111213141516
17181920212223
24252627282930
31      
Kapı Komşuları
Güzel Siteler
Ivır zıvır
XML Feed

FSF Associate Member: 2040

Powered by PyBlosxom

Valid XHTML 1.0!