C ailesinden gelenler için üç sürpriz

TROIA'nın akış kontrol yapıları (IF, SWITCH, WHILE) tanıdık görünür ama üç yerde bilinçli olarak farklı davranır — ve bu farkları bilmeden yazılan kod, derlenir ama beklenmedik çalışır. Bu yazıda hem sözdizimini hem bu üç sürprizi ele alacağım.

IF: kısa devre değerlendirme yok

IF kosul THEN
    blok
ENDIF;
IF A > 1 && A < THIS.GETMAXVALUE(P1) THEN
    RESULT = 'A bir ile maksimum değer arasında';
ENDIF;

Burada asıl kritik detay şu: TROIA, bir mantıksal ifadenin sonucu ilk koşuldan zaten belli olsa bile ifadenin tamamını değerlendirir. Yani C#, Java ya da JavaScript'te alışık olduğunuz "kısa devre" (short-circuit) davranışı burada yok. Pratikte bu şu anlama geliyor: A != NULL && A.ALAN > 0 gibi bir ifade yazarsanız ve A gerçekten null-benzeri bir durumdaysa, ikinci koşul yine de değerlendirilir — birçok dilde alışık olduğunuz "ilk koşul false ise ikincisine hiç bakılmaz, güvenlidir" varsayımı burada geçerli değil. Bir fonksiyon çağrısını güvenli hale getirmek için önce ayrı bir IF ile kontrol edip, güvenliyse iç içe ikinci bir IF yazmak gerekebilir.

ELSE var, ELSEIF yok

IF A == 1 THEN
    RESULT = 'A bire eşit';
ELSE
    RESULT = 'A birden farklı';
ENDIF;

TROIA'da ELSEIF desteklenmiyor; birden fazla koşulu sırayla test etmek istediğinizde ELSE bloğunun içine iç içe IF yazmanız gerekiyor:

IF DURUM == 'BEKLEMEDE' THEN
    RESULT = 'Bekliyor';
ELSE
    IF DURUM == 'ONAYLANDI' THEN
        RESULT = 'Onaylandı';
    ELSE
        RESULT = 'Bilinmiyor';
    ENDIF;
ENDIF;

Beş altı koşullu bir zincirde bu iç içelik hızla okunabilirliği düşürür — bu noktada SWITCH'e geçmek genelde daha temiz bir çözüm.

SWITCH: değişken şart, ifade yasak; BREAK gereksiz

SWITCH DEGISKEN
CASE 'deger1','deger2':
    blok1
DEFAULT:
    blok2
ENDSWITCH;

İki önemli kısıtlama var: SWITCH ifadesinin hedefi sadece bir değişken olabilir, bir ifade (expression) olamaz — yani SWITCH A + B yazamazsınız, önce sonucu bir değişkene atamanız gerekir. İkincisi, ve C ailesinden gelenler için asıl şaşırtıcı olan: BREAK anahtar kelimesi SWITCH içinde ne desteklenir ne de gereklidir — her CASE bloğu kendiliğinden sonlanır, C#'taki gibi bir sonraki case'e "düşme" (fall-through) davranışı yoktur.

VAR = '8';
SWITCH VAR
CASE '7','8':
    RESULT = 'Yedi ya da sekiz';
DEFAULT:
    RESULT = 'Ne olduğunu bilmiyorum.';
ENDSWITCH;

WHILE ve LOOP AT

WHILE kosul
BEGIN
    blok
ENDWHILE;
SAYAC = 0;
WHILE SAYAC < 10
BEGIN
    IF SAYAC % 2 == 0 THEN
        RESULT = RESULT + SAYAC + ':çift, ';
    ENDIF;
    SAYAC = SAYAC + 1;
ENDWHILE;

Tablo satırları üzerinde gezinmek için ayrı bir komut var: LOOP AT {tablo} BEGIN ... ENDLOOP; — belgelerin kendi tabiriyle, şimdilik "satır bazlı bir foreach" olarak düşünülebilir (performans varyasyonlarını table-ve-loop-neden-bu-kadar-hizli yazısında ayrıca ele aldım).

BREAK ve CONTINUE

INDEKS = 0;
WHILE 1 == 1
BEGIN
    INDEKS = INDEKS + 1;
    IF INDEKS == 10 THEN
        BREAK;
    ENDIF;
    IF INDEKS % 2 == 0 THEN
        CONTINUE;
    ENDIF;
    /* burada sadece tek sayılar için çalışan kod */
ENDWHILE;

CONTINUE döngünün kalan kısmını atlayıp bir sonraki iterasyona geçer, BREAK döngüyü tamamen sonlandırır — bu ikisi TROIA'da diğer dillerdeki gibi tanıdık davranıyor; sürpriz olan kısım yukarıda bahsettiğim IF'in kısa devre yapmaması ve SWITCH'in BREAK istememesi.

Sık yapılan hatalar

  • Kısa devre bekleyerek güvensiz bir ifade yazmak — A != '' && FONKSIYON(A) gibi bir ifadede FONKSIYON'un boş A ile çağrılmasının güvenli olmadığını varsayıp bu tek satırı yazmak; TROIA'da ikisi de değerlendirileceği için FONKSIYON her koşulda çağrılır.
  • SWITCH içine BREAK eklemeyi unutmamak diye endişelenmek — aslında endişelenecek bir şey yok, BREAK burada gereksiz; C#'tan gelenlerin refleks olarak eklediği BREAK'ler burada anlam ifade etmez (hataya da yol açmaz, sadece gereksizdir).
  • Beş altı seviyeli iç içe IF/ELSE yazıp okunabilirliği kaybetmek — ELSEIF olmadığı için bu tuzağa düşmek kolay; üç ya da daha fazla ayrık durum söz konusuysa SWITCH'e geçmek kodu belirgin şekilde sadeleştirir.

Sıradaki adım

Sıradaki yazıda dialog'ların BEFORE, AFTER, ONSHOW olaylarına ve CALL DIALOG ile ekranlar arası geçişe değineceğim.