İş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.