D.ershane D Programlama Dili Dersleri

belirsiz sayıda parametre: [variadic], aynı işlevi değişik sayıda parametreyle çağırabilme olanağı
gösterge: [pointer], bir değişkeni gösteren değişken
varsayılan: [default], özellikle belirtilmediğinde kullanılan
... bütün sözlük

Bölümler
İngilizce Kaynaklar
Diğer



Parametre Serbestliği

Bu bölümde İşlev Parametreleri dersinde anlatılanlarla doğrudan ilgili olan ve işlev çağırma konusunda bazı serbestlikler sağlayan iki olanağı göstereceğim:

Varsayılan parametre değerleri

İşlevlerle ilgili bir kolaylık, parametrelere varsayılan değerler atanabilmesidir. Bu, yapı üyelerinin varsayılan değerlerinin belirlenebilmesine benzer.

Bazı işlevlerin bazı parametreleri çoğu durumda hep aynı değerle çağrılıyor olabilirler. Örnek olarak, bir eşleme tablosunu çıkışa yazdıran bir işlev düşünelim. Yazdırdığı eşleme tablosunun hem indeks türü hem de değer türü string olsun. Bu işlev, çıktıda kullanacağı ayraç karakterlerini de parametre olarak alacak şekilde esnek tasarlanmış olsun:

void tabloYazdır(in char[] başlık,
                 in string[string] tablo,
                 in char[] indeksAyracı,
                 in char[] elemanAyracı)
{
    dout.writefln("-- ", başlık, " --");

    const char[][] indeksler = tablo.keys;

    indeksler.sort;

    foreach (sayaç, indeks; indeksler) {

        // İlk elemandan önce ayraç olmamalı
        if (sayaç != 0) {
            dout.writef(elemanAyracı);
        }

        dout.writef(indeks, indeksAyracı, tablo[indeks]);
    }

    dout.writefln();
}

O işlev, indekslerle değerler arasına ":", elemanlar arasına da ", " gelecek şekilde şöyle çağrılabilir:

    string[string] sözlük = [
        "mavi":"blue", "kırmızı":"red", "gri":"gray" ];

    tabloYazdır("Türkçe'den İngilizce'ye Renkler",
                sözlük, ":", ", ");
-- Türkçe'den İngilizce'ye Renkler --
gri:gray, kırmızı:red, mavi:blue

Aynı programda başka tabloların da yazdırıldıklarını, ve çoğu durumda hep aynı ayraçların kullanıldıklarını varsayalım. Yalnızca bazı özel durumlarda farklı ayraçlar kullanılıyor olsun.

Parametre değerlerinin çoğunlukla aynı değeri aldıkları durumlarda, o değerler varsayılan değer olarak belirtilebilirler:

void tabloYazdır(in char[] başlık,
                 in string[string] tablo,
                 in char[] indeksAyracı= ":",
                 in char[] elemanAyracı= ", ")
{
    // ...
}

Varsayılan değerleri olan parametreler, işlev çağrısı sırasında belirtilmeyebilirler:

    tabloYazdır("Türkçe'den İngilizce'ye Renkler",
                sözlük);   /* ← ayraçlar belirtilmemiş;
                            *   varsayılan değerlerini alırlar
                            */

O durumda, belirtilmeyen parametrelerin varsayılan değerleri kullanılır.

Normalin dışında değer kullanılacağı durumlarda işlev çağrılırken o parametreler için yine de özel değerler verilebilir. Gerekiyorsa yalnızca ilki:

    tabloYazdır("Türkçe'den İngilizce'ye Renkler",
                sözlük, "=");
-- Türkçe'den İngilizce'ye Renkler --
gri=gray, kırmızı=red, mavi=blue

Veya gerekiyorsa her ikisi birden:

    tabloYazdır("Türkçe'den İngilizce'ye Renkler",
                sözlük, "=", "\n");
-- Türkçe'den İngilizce'ye Renkler --
gri=gray
kırmızı=red
mavi=blue

Varsayılan değerler yalnızca parametre listesinin son tarafındaki parametreler için belirtilebilir. Baştaki veya aradaki parametrelerin varsayılan değerleri belirtilemez.

Belirsiz sayıda parametreler

Varsayılan parametre değerleri, işlevin aslında kaç tane parametre aldığını değiştirmez. Örneğin yukarıdaki tabloYazdır işlevi her zaman için dört adet parametre alır; ve işini yaparken o dört parametreyi kullanır.

D'nin başka bir olanağı, işlevleri belirsiz sayıda parametre ile çağırabilmemizi sağlar. Bu olanağı aslında daha önce de çok kere kullandık. Örneğin writefln'i hiçbir kısıtlamayla karşılaşmadan sınırsız sayıda parametre ile çağırabiliyorduk:

    dout.writefln(
        "merhaba", 7, "dünya", 9.8 /*, ve istediğimiz kadar
                                    *  daha parametre */);

D'de belirsiz sayıda parametre kullanmanın üç yolu vardır:

D, belirsiz sayıdaki parametreleri o tür işlevlere bir dizi halinde sunar. Belirsiz sayıda parametre alacak olan işlevin parametresi olarak bir dizi belirtilir ve hemen arkasından ... karakterleri yazılır:

import std.cstream;

double topla(in double[] sayılar ...)
{
    double sonuç = 0.0;

    foreach (sayı; sayılar) {
        sonuç += sayı;
    }

    return sonuç;
}

O şekilde tanımlanan topla, belirsiz sayıda parametre alan bir işlev haline gelmiş olur. Onu istediğimiz sayıda double ile çağırabiliriz:

    dout.writefln(topla(1.1, 2.2, 3.3));

Parametre listesindeki dizi ve ondan sonra gelen ... karakterleri, işlevin çağrıldığı sırada kullanılan parametreleri temsil ederler. topla işlevi örneğin 5 double parametre ile çağrıldığında, topla'nın sayılar parametresi o beş sayıyı içerir.

Böyle işlevlerin şart koştukları parametreleri de bulunabilir. Örneğin belirsiz sayıdaki sözcüğü parantezler arasında yazdıran bir işlev düşünelim. Bu işlev, her ne kadar sözcük sayısını serbest bıraksa da, ne tür parantezler kullanılacağının belirtilmesini şart koşsun.

Kesinlikle belirtilmesi gereken parametreler parametre listesinde baş tarafa yazılırlar. Belirsiz sayıdaki parametreyi temsil eden dizi ise en sona yazılır:

char[] parantezle(
    in char[] açma,     // ← işlev çağrılırken belirtilmelidir
    in char[] kapama,   // ← işlev çağrılırken belirtilmelidir
    in char[][] sözcükler ...)      // ← hiç belirtilmeyebilir
{
    char[] sonuç;

    foreach (sözcük; sözcükler) {
        sonuç ~= açma;
        sonuç ~= sözcük;
        sonuç ~= kapama;
    }

    return sonuç;
}

O işlevi çağırırken ilk iki parametre mutlaka belirtilmelidir:

    parantezle("{");     // ← derleme HATASI

Kesinlikle belirtilmeleri gereken baştaki parametreler verildiği sürece, geri kalan parametreler konusunda serbestlik vardır. Buna uygun olarak açma ve kapama parantezlerini kullanan bir örnek:

  dout.writefln(parantezle("{", "}", "elma", "armut", "muz"));
{elma}{armut}{muz}
Problem
  • Daha önce gördüğümüz şu enum türünün tanımlı olduğunu varsayın:
  • enum İşlem { Toplama, Çıkarma, Çarpma, Bölme }
    

    O işlem çeşidini ve işlemde kullanılacak iki kesirli sayıyı içeren bir de yapı olsun:

    struct Hesap
    {
        İşlem işlem;
        double birinci;
        double ikinci;
    }
    

    Örneğin Hesap(İşlem.Bölme, 7.7, 8.8) nesnesi, 7.7'nin 8.8'e bölüneceği anlamına gelsin.

    Bu yapı nesnelerinden belirsiz sayıda parametre alan, her birisini teker teker hesaplayan, ve bütün sonuçları bir double dizisi olarak döndüren hesapla isminde bir işlev yazın.

    Bu işlev örneğin şöyle çağrılabilsin:

    void main()
    {
        dout.writefln(hesapla(Hesap(İşlem.Toplama, 1.1, 2.2),
                              Hesap(İşlem.Çıkarma, 3.3, 4.4),
                              Hesap(İşlem.Çarpma, 5.5, 6.6),
                              Hesap(İşlem.Bölme, 7.7, 8.8)));
    }
    

    Yukarıdaki gibi kullanıldığında, hesapla'nın işlem sonuçlarını yerleştirdiği dizi writefln tarafından çıktıya şöyle yazdırılacaktır:

    [3.3,-1.1,36.3,0.875]
    
... çözüm