Fonksiyonlar

Fonksiyonlar Cairo kodunda yaygındır. Dilin en önemli fonksiyonlarından birini zaten gördünüz: birçok programın giriş noktası olan main fonksiyonu. Ayrıca yeni fonksiyonlar bildirmenize izin veren fn anahtar kelimesini de gördünüz.

Cairo kodu, fonksiyon ve değişken adları için geleneksel stil olarak snake case kullanır, burada tüm harfler küçük harftir ve alt çizgiler kelimeleri ayırır. İşte bir fonksiyon tanımı içeren bir program örneği:

fn another_function() {
    println!("Another function.");
}

fn main() {
    println!("Hello, world!");
    another_function();
}

Bir fonksiyonu Cairo'da fn'yi takiben bir fonksiyon adı ve bir parantez kümesi girerek tanımlarız. Süslü parantezler, fonksiyon gövdesinin nerede başlayıp bittiğini derleyiciye söyler.

Tanımladığımız herhangi bir fonksiyonu, adını takiben bir parantez kümesi girerek çağırabiliriz. Programda another_function tanımlandığından, main fonksiyonunun içinden çağrılabilir. Kaynak kodda another_functionmain fonksiyonundan önce tanımladığımızı unutmayın; bunu sonrasında da tanımlayabilirdik. Cairo, fonksiyonlarınızı nerede tanımladığınızı umursamaz, yeter ki çağıran tarafından görülebilecek bir kapsamda tanımlansınlar.

Fonksiyonları daha fazla keşfetmek için Scarb ile functions adında yeni bir proje başlatalım. another_function örneğini src/lib.cairo içine yerleştirin ve çalıştırın. Aşağıdaki çıktıyı görmelisiniz:

$ scarb cairo-run 
   Compiling no_listing_15_functions v0.1.0 (listings/ch02-common-programming-concepts/no_listing_15_functions/Scarb.toml)
    Finished `dev` profile target(s) in 4 seconds
     Running no_listing_15_functions
Hello, world!
Another function.
Run completed successfully, returning []

The lines execute in the order in which they appear in the main function. First the Hello, world! message prints, and then another_function is called and its message is printed.

Parametreler

Fonksiyonları, fonksiyonun imzasının bir parçası olan özel değişkenler olan parametreler ile tanımlayabiliriz. Bir fonksiyonun parametreleri olduğunda, bu parametreler için somut değerler sağlayabilirsiniz. Teknik olarak, somut değerlere argümanlar denir, ancak günlük konuşmada, insanlar genellikle fonksiyonun tanımındaki değişkenler veya fonksiyonu çağırdığınızda geçirilen somut değerler için parametre ve argüman kelimelerini birbirinin yerine kullanır.

another_function'un bu versiyonunda bir parametre ekliyoruz

fn main() {
    another_function(5);
}

fn another_function(x: felt252) {
    println!("The value of x is: {}", x);
}

Bu programı çalıştırmayı deneyin; aşağıdaki çıktıyı almalısınız

$ scarb cairo-run 
   Compiling no_listing_16_single_param v0.1.0 (listings/ch02-common-programming-concepts/no_listing_16_single_param/Scarb.toml)
    Finished `dev` profile target(s) in 4 seconds
     Running no_listing_16_single_param
The value of x is: 5
Run completed successfully, returning []

another_function'ın bildirimi x adında bir parametreye sahiptir. x'in türü felt252 olarak belirtilmiştir. another_function'a 5 geçirdiğimizde, println! makrosu x'i içeren süslü parantez çiftinin olduğu yere format dizgisinde 5 koyar.

Fonksiyon imzalarında, her parametrenin türünü mutlaka belirtmelisiniz. Bu, Cairo’nun tasarımında bilinçli bir karardır: fonksiyon tanımlarında tür annotasyonları gerektirmek, kodun geri kalanında ne tür bir şey kastettiğinizi anlamak için derleyicinin neredeyse hiç sizin tarafınızdan bunları kullanmanızı gerektirmez. Derleyici, fonksiyonun hangi türleri beklediğini bildiğinde daha yardımcı hata mesajları verebilir.

Birden fazla parametre tanımlarken, parametre bildirimlerini aşağıdaki gibi virgülle ayırın:

fn main() {
    print_labeled_measurement(5, "h");
}

fn print_labeled_measurement(value: u128, unit_label: ByteArray) {
    println!("The measurement is: {value}{unit_label}");
}

Bu örnek, value ve unit_label adında iki parametre ile print_labeled_measurement adında bir fonksiyon oluşturur. İlk parametre value adını taşır ve bir u128'dir. İkincisi unit_label adında ve ByteArray türündedir - Cairo'nun string literallerini temsil etmek için içsel türü. Fonksiyon daha sonra hem value hem de unit_label içeren bir metin yazdırır.

Bu kodu çalıştırmayı deneyelim. functions projesinin src/lib.cairo dosyasındaki mevcut programı önceki örnekle değiştirin ve scarb cairo-run kullanarak çalıştırın:

$ scarb cairo-run 
   Compiling no_listing_17_multiple_params v0.1.0 (listings/ch02-common-programming-concepts/no_listing_17_multiple_params/Scarb.toml)
    Finished `dev` profile target(s) in 5 seconds
     Running no_listing_17_multiple_params
The measurement is: 5h
Run completed successfully, returning []

Fonksiyonu value için 5 değeriyle ve unit_label için "h" değeriyle çağırdığımız için, program çıktısı bu değerleri içerir.

Named Parameters

Cairo'da, adlandırılmış parametreler bir fonksiyonu çağırırken argümanların isimlerini belirtmenize olanak tanır. Bu, fonksiyon çağrılarını daha okunabilir ve kendini açıklayıcı hale getirir. Eğer adlandırılmış parametreleri kullanmak istiyorsanız, parametrenin adını ve ona geçirmek istediğiniz değeri belirtmeniz gereklidir. Sözdizimi şöyledir: parameter_name: value. Eğer parametre ile aynı ada sahip bir değişken geçirirseniz, parameter_name: variable_name yerine sadece :parameter_name yazabilirsiniz.

İşte bir örnek

fn foo(x: u8, y: u8) {}

fn main() {
    let first_arg = 3;
    let second_arg = 4;
    foo(x: first_arg, y: second_arg);
    let x = 1;
    let y = 2;
    foo(:x, :y)
}

Statements ve Expressions

Fonksiyon gövdeleri, isteğe bağlı olarak bir expression ile sonlanan bir dizi statement'tan oluşur. Şu ana kadar ele aldığımız fonksiyonlar, bir sonlandırma expression'ı içermemişti, ancak bir statement içinde bir expression görmüşsünüzdür. Cairo bir expression tabanlı dil olduğundan, bu önemli bir ayrımı anlamak önemlidir. Diğer diller aynı ayrımlara sahip değildir, bu yüzden statement'lar ve expression'lar neyi ifade eder ve farklılıkları fonksiyon gövdelerini nasıl etkiler, bunlara bakalım.

  • Statements, bazı eylemleri gerçekleştiren ve bir değer döndürmeyen talimatlardır.
  • Expressions bir sonuç değerine göre değerlendirilir. Şimdi bazı örneklere bakalım.

Biz aslında zaten statement'lar ve expression'lar kullanmışızdır. Bir değişken oluşturmak ve let anahtar kelimesi ile ona bir değer atamak bir statement'dır. Listing 2-1'de, let y = 6; bir statement'dır.

fn main() {
    let y = 6;
}

Listing 2-1: A main function declaration containing one statement

Fonksiyon tanımları da ifadelerdir; önceki örneğin tamamı kendi içinde bir ifadedir.

Statement'lar değer döndürmez. Bu nedenle, bir let statement'ını başka bir değişkene atayamazsınız, aşağıdaki kodun yapmaya çalıştığı gibi; bir hata alırsınız:

fn main() {
    let x = (let y = 6);
}

Bu programı çalıştırdığınızda alacağınız hata şuna benzer:

$ scarb cairo-run 
   Compiling no_listing_18_statements_dont_return_values v0.1.0 (listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/Scarb.toml)
error: Missing token TerminalRParen.
 --> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:14
    let x = (let y = 6);
             ^

error: Missing token TerminalSemicolon.
 --> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:14
    let x = (let y = 6);
             ^

error: Missing token TerminalSemicolon.
 --> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:23
    let x = (let y = 6);
                      ^

error: Skipped tokens. Expected: statement.
 --> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:23
    let x = (let y = 6);
                      ^^

warn[E0001]: Unused variable. Consider ignoring by prefixing with `_`.
 --> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:9
    let x = (let y = 6);
        ^

warn[E0001]: Unused variable. Consider ignoring by prefixing with `_`.
 --> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:18
    let x = (let y = 6);
                 ^

error: could not compile `no_listing_18_statements_dont_return_values` due to previous error
error: `scarb metadata` exited with error

let y = 6 statement'ı bir değer döndürmez, bu yüzden x'in bağlanacak bir şeyi yoktur. Bu, C ve Ruby gibi diğer dillerde olanlardan farklıdır, bu dillerde atama, atamanın değerini döndürür. Bu dillerde x = y = 6 yazabilir ve hem x hem de y'nin değeri 6 olur; bu Cairo'da söz konusu değildir.

Expression'lar bir değere değerlendirilir ve Cairo'da yazacağınız kodun çoğunu oluşturur. Örneğin, 5 + 6 gibi bir matematik işlemi, değeri 11 olarak değerlendirilen bir expression'dır. Expression'lar statement'ların bir parçası olabilir: Listing 2-1'de, let y = 6; statement'ındaki 6, değeri 6 olarak değerlendirilen bir expression'dır.

Bir fonksiyonu çağırmak bir expression'dır çünkü her zaman bir değere değerlendirilir: belirtilmişse fonksiyonun açık dönüş değeri, aksi takdirde 'unit' türü ().

Örneğin, küme parantezleriyle oluşturulan yeni bir kapsam bloğu bir ifadedir:

fn main() {
    let y = {
        let x = 3;
        x + 1
    };

    println!("The value of y is: {}", y);
}

Bu expression:

    let y = {
        let x = 3;
        x + 1
    };

bu durumda 4 olarak değerlendirilen bir bloktur. Bu değer, let statement'ının bir parçası olarak y'e bağlanır. x + 1 satırının sonunda, şimdiye kadar gördüğünüz çoğu satırdan farklı olarak bir noktalı virgül yoktur. Expression'lar sonunda noktalı virgül içermez. Bir expression'ın sonuna noktalı virgül eklerseniz, onu bir statement'a dönüştürürsünüz ve o zaman bir değer döndürmez. Bunu, fonksiyon dönüş değerlerini ve expression'ları keşfederken aklınızda bulundurun.

Dönüş Değerleri Olan Fonksiyonlar

Fonksiyonlar, onları çağıran koda değerler döndürebilir. Dönüş değerlerini isimlendirmeyiz, ancak türlerini bir ok (->) sonrasında belirtmeliyiz. Cairo'da, fonksiyonun dönüş değeri, fonksiyonun gövde bloğundaki son expression ile eşanlamlıdır. return anahtar kelimesini kullanarak ve bir değer belirterek bir fonksiyondan erken dönüş yapabilirsiniz, ancak çoğu fonksiyon son expression'ı dolaylı olarak döndürür. İşte bir değer döndüren bir fonksiyonun örneği:

fn five() -> u32 {
    5
}

fn main() {
    let x = five();
    println!("The value of x is: {}", x);
}

five fonksiyonunda fonksiyon çağrıları veya hatta let statement'ları yoktur—sadece tek başına 5 numarası vardır. Bu, Cairo'da geçerli bir fonksiyondur. Fonksiyonun dönüş türünün de -> u32 olarak belirtildiğine dikkat edin. Bu kodu çalıştırmayı deneyin; çıktı şöyle görünmelidir:

$ scarb cairo-run 
   Compiling no_listing_20_function_return_values v0.1.0 (listings/ch02-common-programming-concepts/no_listing_22_function_return_values/Scarb.toml)
    Finished `dev` profile target(s) in 4 seconds
     Running no_listing_20_function_return_values
The value of x is: 5
Run completed successfully, returning []

five içindeki 5, fonksiyonun dönüş değeridir, bu yüzden dönüş türü u32'dir. Bunu daha detaylı inceleyelim. İki önemli nokta var: ilk olarak, let x = five(); satırı, bir fonksiyonun dönüş değerini bir değişkeni başlatmak için kullandığımızı gösterir. Çünkü five fonksiyonu bir 5 döndürür, bu satır aşağıdakiyle aynıdır:

let x = 5;

İkincisi, five fonksiyonunun parametreleri yoktur ve dönüş değerinin türünü tanımlar, ancak fonksiyonun gövdesi, döndürmek istediğimiz değerin bir expression'ı olduğu için noktalı virgül olmadan yalnız bir 5'tir. Bir başka örneğe bakalım:

fn main() {
    let x = plus_one(5);

    println!("The value of x is: {}", x);
}

fn plus_one(x: u32) -> u32 {
    x + 1
}

Bu kodu çalıştırmak x = 6 yazdıracaktır. Ancak, x + 1 satırının sonuna noktalı virgül koyarsak, bunu bir expression'dan bir statement'a çevirirsek, bir hata alırız:

fn main() {
    let x = plus_one(5);

    println!("The value of x is: {}", x);
}

fn plus_one(x: u32) -> u32 {
    x + 1;
}
$ scarb cairo-run 
   Compiling no_listing_22_function_return_invalid v0.1.0 (listings/ch02-common-programming-concepts/no_listing_24_function_return_invalid/Scarb.toml)
error: Unexpected return type. Expected: "core::integer::u32", found: "()".
 --> listings/ch02-common-programming-concepts/no_listing_24_function_return_invalid/src/lib.cairo:9:28
fn plus_one(x: u32) -> u32 {
                           ^

error: could not compile `no_listing_22_function_return_invalid` due to previous error
error: `scarb metadata` exited with error

Ana hata mesajı olan Unexpected return type, bu kodla ilgili temel sorunu ortaya koyar. plus_one fonksiyonunun tanımı, bir u32 döndüreceğini söyler, ancak statement'lar bir değere değerlendirilmez, bu (), yani unit türü ile ifade edilir. Dolayısıyla, hiçbir şey döndürülmez, bu da fonksiyonun tanımı ile çelişir ve bir hataya neden olur.