D Programlama Dili – Programlama dersleri ve D referansı
Ali Çehreli

aralık: [range], belirli biçimde erişilen bir grup eleman
işlev: [function], programdaki bir kaç adımı bir araya getiren program parçası
sarma: [encapsulation], üyelere dışarıdan erişimi kısıtlamak
şablon: [template], derleyicinin örneğin 'türden bağımsız programlama' için kod üretme düzeneği
üye işlev: [member function], yapı veya sınıfın kendi tanımladığı işlemleri
... bütün sözlük



İngilizce Kaynaklar


Diğer




İşlev Çağırma Ortak Söz Dizimi (UFCS)

UFCS "universal function call syntax"in kısaltmasıdır. Normal işlevlerin üye işlevler gibi çağrılabilmelerini sağlar. Derleyicinin otomatik olarak sağladığı bu olanak çok kısa olarak iki ifade ile anlatılabilir:

    değişken.işlev(parametre_değerleri)

Yukarıdaki gibi bir ifade ile karşılaşıldığında eğer değişken'in o parametrelere uyan işlev isminde bir üye işlevi yoksa, derleyici hata vermeden önce bir de aşağıdaki normal işlev çağrısını dener:

    işlev(değişken, parametre_değerleri)

Eğer derlenebiliyorsa o ifade kabul edilir ve aslında normal bir işlev olan işlev sanki bir üye işlevmiş gibi çağrılmış olur.

Not: Bu olanak yalnızca modül düzeyinde tanımlanmış olan işlevlerle kullanılabilir; örneğin, iç işlevler UFCS söz dizimi ile çağrılamazlar.

Belirli bir yapı veya sınıf türünü yakından ilgilendiren işlevlerin o türün üye işlevleri olarak tanımlandıklarını biliyoruz. Her normal işlev özel üyelere erişemediğinden üye işlevler sarma kavramı için gereklidir. Örneğin, private olarak işaretlenmiş olan üyelere ancak o türün kendi üye işlevleri ve o türü içeren modül tarafından erişilebilir.

Deposundaki benzin miktarını da bildiren bir Araba türü olsun:

class Araba {
    enum ekonomi = 12.5;           // litre başına km (ortalama)
    private double kalanBenzin;    // litre

    this(double kalanBenzin) {
        this.kalanBenzin = kalanBenzin;
    }

    double benzin() const {
        return kalanBenzin;
    }

    // ...
}

Üye işlevler ne kadar yararlı ve gerekli olsalar da, belirli bir tür üzerindeki olası bütün işlemlerin üye işlevler olarak tanımlanmaları beklenmemelidir çünkü bazı işlemler ancak belirli programlarda anlamlıdırlar veya yararlıdırlar. Örneğin, arabanın belirli bir mesafeyi gidip gidemeyeceğini bildiren işlevin üye işlev olarak değil, normal işlev olarak tanımlanması daha uygun olabilir:

bool gidebilir_mi(Araba araba, double mesafe) {
    return (araba.benzin() * araba.ekonomi) >= mesafe;
}

Doğal olarak, bu işlemin serbest işlev olarak tanımlanmış olması işlev çağrısı söz diziminde farklılık doğurur. Değişkenin ismi aşağıdaki iki kullanımda farklı yerlerde geçmektedir:

void main() {
    auto araba = new Araba(5);

    auto kalanBenzin = araba.benzin(); // Üye işlev söz dizimi

    if (gidebilir_mi(araba, 100)) {    // Normal işlev söz dizimi
        // ...
    }
}

UFCS, söz dizimindeki bu farklılığı ortadan kaldırır; normal işlevlerin de üye işlevler gibi çağrılabilmelerini sağlar:

    if (araba.gidebilir_mi(100)) { // Normal işlev, üye işlev söz dizimi ile
        // ...
    }

Bu olanak hazır değerler dahil olmak üzere temel türlerle de kullanılabilir:

int yarısı(int değer) {
    return değer / 2;
}

void main() {
    assert(42.yarısı() == 21);
}

Bir sonraki bölümde göreceğimiz gibi, işlev çağrısı sırasında parametre değeri kullanılmadığında o işlev parantezsiz olarak da çağrılabilir. Bu olanaktan da yararlanıldığında yukarıdaki ifade daha da kısalır. Sonuçta, aşağıdaki satırların üçü de aynı anlamdadır:

    sonuç = yarısı(değer);
    sonuç = değer.yarısı();
    sonuç = değer.yarısı;

UFCS özellikle işlevlerin zincirleme olarak çağrıldığı durumlarda yararlıdır. Bunu int dizileri ile işleyen üç işlev üzerinde görelim:

// Bütün elemanların 'bölen' ile bölünmüşlerini döndürür
int[] bölümleri(int[] dilim, int bölen) {
    int[] sonuç;
    sonuç.reserve(dilim.length);

    foreach (değer; dilim) {
        sonuç ~= değer / bölen;
    }

    return sonuç;
}

// Bütün elemanların 'çarpan' ile çarpılmışlarını döndürür
int[] çarpımları(int[] dilim, int çarpan) {
    int[] sonuç;
    sonuç.reserve(dilim.length);

    foreach (değer; dilim) {
        sonuç ~= değer * çarpan;
    }

    return sonuç;
}

// Elemanların çift olanlarını döndürür
int[] çiftleri(int[] dilim) {
    int[] sonuç;
    sonuç.reserve(dilim.length);

    foreach (değer; dilim) {
        if (!(değer % 2)) {
            sonuç ~= değer;
        }
    }

    return sonuç;
}

UFCS'ten yararlanılmadığı zaman bu üç işlevi zincirleme olarak çağırmanın bir yolu aşağıdaki gibidir:

import std.stdio;

// ...

void main() {
    auto sayılar = [ 1, 2, 3, 4, 5 ];
    writeln(çiftleri(bölümleri(çarpımları(sayılar, 10), 3)));
}

Sayılar önce 10 ile çarpılmakta, sonra 3 ile bölünmekte, ve sonucun çift olanları kullanılmaktadır:

[6, 10, 16]

Yukarıdaki ifadenin bir sorunu, çarpımları ile 10'un ve bölümleri ile 3'ün birbirleriyle ilgili olmalarına rağmen ifadede birbirlerinden uzakta yazılmak zorunda olmalarıdır. UFCS bu sorunu ortadan kaldırır ve işlem sıralarına uyan daha doğal bir söz dizimi getirir:

    writeln(sayılar.çarpımları(10).bölümleri(3).çiftleri);

Bazı programcılar writeln gibi çağrılarda da UFCS'ten yararlanırlar:

    sayılar.çarpımları(10).bölümleri(3).çiftleri.writeln;

Ek bir bilgi olarak, yukarıdaki bütün program map ve filter'dan yararlanılarak da yazılabilir:

import std.stdio;
import std.algorithm;

void main() {
    auto sayılar = [ 1, 2, 3, 4, 5 ];

    writeln(sayılar
            .map!(a => a * 10)
            .map!(a => a / 3)
            .filter!(a => !(a % 2)));
}

Bunu sağlayan şablon, aralık, ve isimsiz işlev olanaklarını daha sonraki bölümlerde göreceğiz.