main'in Parametreleri ve Dönüş Değeri
İşlevleri anlatırken main'in de bir işlev olduğunu söylemiştim. Programın işleyişi main'le başlar ve oradan başka işlevlere dallanır. main'in şimdiye kadar gördüğümüz tanımı şöyleydi:
void main()
O tanıma bakarak main'in bir değer döndürmediğini ve hiçbir parametre almadığını düşünürüz. Aslında bu mümkün değildir, çünkü programı başlatan ortam bir dönüş değeri bekler; main, void döndürüyor gibi yazılmış olsa da aslında bir değer döndürür.
main'in dönüş değeri
Programlar her zaman için başka bir ortam tarafından başlatılırlar. Bu ortam, programı ismini yazıp Enter'a basarak başlattığımız uç birim olabilir, menülerindeki "Çalıştır" gibi bir komutla başlattığımız bir geliştirme ortamı olabilir, programı kendisi başlatan başka bir program olabilir, vs.
Programımız, kendisini başlatan bu ortama işini başarıyla tamamlayıp tamamlamadığı bilgisini, main'in dönüş değeri olarak bildirir.
Programın dönüş değeri olarak 0 değeri, programın başarıyla sonuçlandığını; 0'dan başka bir değer ise programın çalışması sırasında bir hata oluştuğunu bildirmek için kullanılır. Programlarımızdan istediğimiz değeri döndürmek bize kalmış olsa da, 0'ın başarı anlamına gelmesi standartlaşmıştır.
Not: Dönüş değeri olarak ancak [0,127] aralığındaki tamsayılara güvenebilirsiniz. Bunun dışındaki değerler her ortam tarafından desteklenmiyor olabilir.
Sıfırdan başka değerler her programa göre değişik anlamlar taşıyabilir. Örneğin Unix türevi ortamlarda dosyaları listelemek için kullanılan ls programı; önemsiz hatalarda 1 değerini, ciddi hatalarda ise 2 değerini döndürür. Komut satırından başlatılan programların dönüş değerleri $? ortam değişkeninden okunabilir. Örneğin klasörde bulunmayan bir dosyayı görmek istediğimizde, programın dönüş değerini komut satırında $? değişkeninden şöyle okuyabiliriz:
Not: Aşağıdaki komut satırı örneklerinde # karakteriyle başlayan satırlar, kullanıcın yazdığı satırları gösteriyor. Aynı adımları denemek istediğinizde o satırları sizin yazarak Enter'a basmanız gerekir. O satırları daha koyu olarak gösterdim.
Ek olarak, aşağıdaki örnekler bir Linux ortamında denenmiş olsalar da, benzerlerini örneğin Windows DOS pencerelerinde de kullanabilirsiniz.
# ls klasorde_bulunmayan_bir_dosya ls: klasorde_bulunmayan_bir_dosya: No such file or directory # echo $? 2 ← programın dönüş değeri
Dönüş değeri void olan main'ler de değer üretirler
Şimdiye kadar karşılaştığımız işlevlerin bazılarının işlerini yapamayacakları durumlara düştüklerinde hata attıklarını görmüştük. Şimdiye kadar gördüğümüz kadarıyla, hata atıldığı zaman program bir object.Exception mesajıyla sonlanıyordu.
Bu gibi durumlarda, main'in dönüş türü olarak void kullanılmış olsa bile, yani main bir değer döndürmeyecekmiş gibi yazılmış olsa bile, programı çalıştıran ortama 1 değeri döndürülür. Bunun bir örneğini görmek için hata atarak sonlanan şu son derece basit programı çalıştıralım:
void main() { throw new Exception("Bir hata oldu"); }
Dönüş türü void olarak tanımlandığı halde programı çalıştıran ortama 1 değeri döndürülmüştür:
# ~/deneme/d/deneme object.Exception: Bir hata oldu # echo $? 1
Benzer şekilde, dönüş türü void olarak tanımlanmış olan main işlevleri başarıyla sonlandıklarında, dönüş değeri olarak 0 üretirler. Yine son derece basit, ama bu sefer başarıyla sonlanan bir program:
import std.cstream; void main() { dout.writefln("Başardım!"); }
# ~/deneme/d/deneme Başardım! # echo $? 0
Kendi dönüş değerlerimizi belirlemek
Kendi programlarımızdan değer döndürmek; başka işlevlerde de olduğu gibi, main'i dönüş türü int olacak şekilde tanımlamak ve bir return satırı kullanmak kadar basittir:
import std.cstream; int main() { int sayı; dout.writef("Lütfen 3-6 arasında bir sayı giriniz: "); din.readf(&sayı); if ((sayı < 3) || (sayı > 6)) { derr.writefln("HATA: ", sayı, " uygun değil!"); return 3; } dout.writefln("Teşekkür: ", sayı); return 0; }
Programın isminin deneme olduğunu kabul edersek ve istenen aralıkta bir sayıyla başlatırsak, programın dönüş değeri 0 olur:
# ./deneme Lütfen 3-6 arasında bir sayı giriniz: 5 Teşekkür: 5 # echo $? 0
Aralığın dışında bir değerle başlatırsak, programdan döndürdüğümüz 3 değerini görürüz:
# ~/deneme/d/deneme Lütfen 3-6 arasında bir sayı giriniz: 10 HATA: 10 uygun değil! # echo $? 3
Normalde hata için 1 değerini kullanmak yeterlidir. Ben yalnızca örnek olması için, ve bir özel nedeni olmadan 3 değerini seçtim.
Standart hata akımı derr
Yukarıdaki programda derr akımını kullandım. Bu akım, standart akımların üçüncüsüdür ve programın hata mesajlarını yazmak için kullanılır:
din: standart giriş akımıdout: standart çıkış akımıderr: standart hata akımı
Programlar komut satırından başlatıldıklarında dout ve derr akımlarına yazılanlar normalde ekranda belirirler. Bu akımlara yazılan mesajları istendiğinde ayrı ayrı elde etmek de mümkündür.
main'in parametreleri
Bazı programlar kendilerini başlatan ortamlardan parametre alabilirler. Örneğin yukarıda gördüğümüz ls programı bazen parametresiz olarak yalnızca ls yazarak başlatılabilir:
# ls
deneme
deneme.d
İsteğe bağlı olarak da bir veya daha çok parametreyle başlatılabilir. Bu parametrelerin anlamları bütünüyle programa bağlıdır ve o programın belgelerinde belirtilmiştir:
# ls -l deneme -rwxr-xr-x 1 acehreli users 460668 Nov 6 20:38 deneme
D programlarını başlatırken kullanılan parametreler, main'e bir string dizisi olarak gönderilirler. main'i string[] parametre alacak şekilde tanımlamak, bu parametrelere erişmek için yeterlidir. Örneğin başlatılırken kendisine verilen parametrelerini çıktısına yazdıran bir program şöyle yazılabilir:
import std.cstream; void main(string[] parametreler) { foreach (i, parametre; parametreler) { dout.writefln("%3s numaralı parametre: %s", i, parametre); } }
# ./deneme komut satirina yazilan parametreler 42 -bir_secenek
0 numaralı parametre: ./deneme
1 numaralı parametre: komut
2 numaralı parametre: satirina
3 numaralı parametre: yazilan
4 numaralı parametre: parametreler
5 numaralı parametre: 42
6 numaralı parametre: -bir_secenek
Parametre dizisinin ilk elemanı, her zaman için program başlatılırken kullanılan program ismidir. Diğer parametreler, bu dizinin geri kalan elemanlarıdır.
Bu parametrelerle ne yapacağı, artık tamamen programa kalmıştır. Örneğin kendisine verilen iki sözcüğü ters sırada yazdıran bir program:
import std.cstream; int main(string[] parametreler) { if (parametreler.length != 3) { derr.writefln( "HATA! Doğru kullanım:\n ", parametreler[0], " bir_sözcük başka_sözcük"); return 1; } dout.writefln(parametreler[2], ' ', parametreler[1]); return 0; }
Yanlış şekilde başlatıldığında uyarıyor da:
# ./deneme HATA! Doğru kullanım: ./deneme bir_sözcük başka_sözcük # ./deneme dünya merhaba merhaba dünya
Program seçenekleri ve std.getopt modülü
main'in parametreleriyle ve dönüş değeriyle ilgili olarak bilinmesi gerekenler aslında bu kadardır. Ancak parametreleri teker teker listeden ayıklamak zahmetli olabilir. Onun yerine, bu konuda yardım alabileceğimiz std.getopt modülünün bir kullanımını göstereceğim.
Bazı parametreler, program tarafından bilgi olarak kullanılırlar. Örneğin yukarıdaki programa verilen "dünya" ve "merhaba" parametreleri, o programın ekrana yazdıracağı bilgiyi belirliyordu.
Bazı parametreler ise programın işini nasıl yapacağını belirlerler; bunlara program seçeneği denir. Örneğin yukarıda kullandığımız ls programına, komut satırında seçenek olarak -l vermiştik.
Program seçenekleri, programların kullanışlılıklarını arttırır. Programın istediği parametreler, teker teker bir kişi tarafından verilmek yerine komut satırından okunurlar. Böylece bu programlar örneğin betik programlar içinden çağrılabilirler.
Komut satırı parametrelerinin ne oldukları ve ne anlama geldikleri her ne kadar tamamen programa bağlı olsalar da, onların da bir standardı gelişmiştir. POSIX standardına uygun bir kullanımda, her seçenek -- ile başlar, seçenek ismi bunlara bitişik olarak yazılır, ve seçenek değeri de bir = karakterinden sonra gelir:
# ./deneme --bir_secenek=17
Phobos'un std.getopt modülü, programa parametre olarak verilen bu tür seçeneklerin ayıklanmasında yardımcı olur. Ben burada fazla ayrıntıya girmeden yalnızca getopt işlevinin bir kullanımını göstereceğim.
Çıkışına rastgele seçtiği sayıları yazdıran bir program tasarlayalım. Kaç tane sayı yazdıracağı ve sayıların değerlerinin hangi aralıkta olacağı programa komut satırından seçenekler olarak verilsin. Program örneğin şu şekilde başlatılabilsin:
# ./deneme --adet=7 --en-kucuk=10 --en-buyuk=15
getopt işlevi, bu değerleri program parametrelerinden seçip çıkartmakta yararlıdır. getopt'a okuduğu değerleri yazacağı değişkenleri, din.readf kullanımından tanıdığımız & karakteriyle bir gösterge olarak bildiririz:
import std.cstream; import std.getopt; import std.random; void main(string[] parametreler) { int adet; int enKüçükDeğer; int enBüyükDeğer; getopt(parametreler, "adet", &adet, "en-kucuk", &enKüçükDeğer, "en-buyuk", &enBüyükDeğer); foreach (i; 0 .. adet) { dout.writef( uniform(enKüçükDeğer, enBüyükDeğer + 1), ' '); } dout.writefln(); }
# ./deneme --adet=7 --en-kucuk=10 --en-buyuk=15
11 11 13 11 14 15 10
Çoğu zaman parametrelerin kestirmeleri de olur. Örneğin --adet yazmak yerine kısaca -a yazılır. Seçeneklerin kestirmeleri, getopt'a | ayracından sonra bildirilir:
getopt(parametreler,
"adet|a", &adet,
"en-kucuk|k", &enKüçükDeğer,
"en-buyuk|b", &enBüyükDeğer);
Artık programı bu kestirme seçeneklerle de başlatabiliriz:
# ./deneme -a=7 -k=10 -b=15
11 13 10 15 14 15 14
Parametrelerin programa string türünde geldiklerini görmüştük. getopt, bu dizgileri değişkenlerin türlerine otomatik olarak dönüştürür. Örneğin yukarıdaki programdaki adet'in int olduğunu bildiği için, onu string'den int'e çevirir. getopt'u kullanmadığımız zamanlarda bu dönüşümü daha önce de bir kaç kere kullandığımız to işleviyle kendimiz de gerçekleştirebiliriz.
std.conv modülünde tanımlanmış olan to'yu daha önce hep tamsayıları string'e dönüştürmek için to!string şeklinde kullanmıştık. string yerine başka bir tür yazarsak, başka türlere de dönüştürebiliriz. Örneğin 0'dan başlayarak kendisine komut satırından bildirilen sayıda ikişer ikişer sayan bir programda to'yu şöyle kullanabiliriz:
import std.cstream; import std.conv; void main(string[] parametreler) { // Parametre verilmediğinde 10 varsayıyoruz int adet = 10; if (parametreler.length > 1) { // Bir parametre verilmiş adet = to!int(parametreler[1]); } foreach (i; 0 .. adet) { dout.writef(i * 2, ' '); } dout.writefln(); }
Program; parametre verilmediğinde 10 adet, verildiğinde ise belirtildiği kadar sayı üretir:
# ./deneme 0 2 4 6 8 10 12 14 16 18 # ./deneme 3 0 2 4
Ortam değişkenleri
Bu konuyla doğrudan ilgili olmasa da, dış dünyadan programa verilen bilgiler oldukları için ortam değişkenlerine de kısaca değinmek istiyorum.
Programları başlatan ortamlar, programların yararlanmaları için ortam değişkenleri de sunarlar. Bu değişkenlere std.process modülündeki getenv işleviyle erişilir. Örneğin çalıştırılacak olan programların hangi klasörlerde arandıklarını belirten PATH değişkenini yazdırmak için:
import std.cstream; import std.process; void main() { dout.writefln(getenv("PATH")); }
# ./deneme
/usr/local/bin:/usr/bin:/home/acehreli/dmd/linux/bin
Başka programları başlatmak
Yine bu konuyla doğrudan ilgili olmasa da; programların başka programları başlatmalarını, ve o programların dönüş türlerini almalarını sağlayan system işlevine de değinmek istiyorum. Bu işlev de std.process modülünde bulunur.
system, kendisine parametre olarak verilen dizgiyi sanki komut satırında yazılmış gibi başlatır ve programın dönüş değerini döndürür:
import std.cstream; import std.process; void main() { const int dönüşDeğeri = system("ls -l deneme"); dout.writefln( "ls programı ", dönüşDeğeri," değerini döndürdü"); }
# ./deneme
-rwxr-xr-x 1 acehreli users 461107 Nov 7 17:33 deneme
ls programı 0 değerini döndürdü
Problemler
- Komut satırından parametre olarak iki değer ve bir işlem karakteri alan bir hesap makinesi yazın. Şöyle çalışsın:
- Hangi programı başlatmak istediğinizi soran, bu programı başlatan ve dönüş türünü bildiren bir program yazın.
# ./deneme 3.4 x 7.8
26.52
Not: * karakterinin komut satırında özel bir anlamı olduğu için ben çarpma için x karakterini seçtim. Siz yine de * kullanmak isterseniz, komut satırında \* şeklinde yazmanız gerekir.
D.ershane
Forum
Wiki
Projeler
Tanıtım
İletişim
Hakları