Yazan : Şadi Evren ŞEKER

Bilgisayar bilimlerinde, programlama dillerini sınıflandırmak ve bir programlama dilinin özelliklerini belirtmek için kullanılan bir terimdir. Genellikle literatürde prosedürel programlama (procedural programming) olarak da geçmektedir.

Emirli programlama ile kastedilen, bir programlama dilindeki komutların satır satır emirlerden oluşmasıdır. Örneğin bir robota komut verecek olsaydık:

  • Kolu 10 derece sağa döndür
  • 2 metre yürü
  • Kolu 20 derece sola döndür

Benzeri emirleri sıralamamız gerekecekti. Bu emirlere bazı kaynaklarda prosedür ismi verilmektedir. Buradaki prosedür ile anlatılmak istenen örneğin kolu döndürme işleminin alt emirlerden oluşmasıdır. Örneğin robotun nasıl bir açıyla vücudunu tutacağı, kolun hangi mafsalının kaç derecelik açı yapacağı ve kolun nasıl hareket edeceği gibi emirler bu alt prosedürlerde belirtilir.

Emirli programlamanın anlamsal olarak tam zıddı ise tanımlamalı programlamadır (veya bildirimli programlamadır , declarative programming). Bu yaklaşıma göre bir programı oluşturan parçalar belirli bir amaca yönelik kodlanır. Örneğin yukarıdaki örneğe devam edecek olursak bir robot programlamada bir odadaki bir masanın üzerinde duran elmayı almak için:

  • Kapıyı aç
  • Kapıdan gir
  • Masayı tespit et
  • Masaya yaklaş
  • Elmayı bul
  • Elmayı al

Benzeri komutlar vermemiz gerecekti. Burada dikkat edilmesi gereken nokta, verdiğimiz komutların birer amaç için verilmiş olmasıdır. Yani tanımlamalı programlamada ne sorusuna, emirli programlamada ise nasıl sorusuna cevap olacak komutlar ön plana çıkar. Elbette kapıyı açmak veya masaya yaklaşmak eylemleri de bir dizi alt hareketlerden oluşmakta ve bu hareketler de sonuçta birer emir olmaktadır ancak tanımlamalı programlama yaklaşımında yazılan her fonksiyon birer amaca hizmet eder ve daha sonra bu fonksiyonlar birleştirilerek modüler bir problem çözümü elde edilir. Öte yandan emirli programlamada hedef emirlerin doğru şekilde ve dizilimde verilmesidir. Emirli programlamadaki her fonksiyon belirli bir başka emirin alt parçalarıdır ve sürekli olarak programcının emir vermesi gerekir.

Bu anlamda tanımlı programlama sanki robotumuzun daha çok şey bilmesini, emirli programlama ise sanki robotun her işleminin emre bağlanması olarak düşünülebilir.

C, C++ veya JAVA gibi diller, ayrıca makine dili veya FORTRAN, Algol gibi ilkel diller yapı olarak emirli dillerdir. Buna karşılık lisp, prolog ve scheme gibi diller tanımlamalı programlamanın (declarative programming) birer örneğidir. Tanımlamalı (declerative) diller, emirli dillerin tersi olarak düşünülebilir.

Örneğin scheme dilinde 1’den verilen sayıya kadar olan sayıları toplayan bir kod aşağıdaki şekilde olacaktır:

(define (topla n) (
      cond (
        ( = n 1) n)
        (else (+ n ( topla (- n 1))))))

Bu yazı şadi evren şeker tarafından yazılmış ve bilgisayarkavramlari.com sitesinde yayınlanmıştır. Bu içeriğin kopyalanması veya farklı bir sitede yayınlanması hırsızlıktır ve telif hakları yasası gereği suçtur.

Buradaki kod parçasında, topla isminde bir tanım yapılmıştır (declaration) ve bu tanımı kullanmak istediğimizde:

(topla 5)

Yukarıdaki çağırma işlemi olmazsa, fonksiyon sadece tanımlanmış olarak kalacak ve hiçbir işe yaramayacaktır. Programcı yukarıdaki tanımı yaptıktan sonra istediği kadar bu tanımı çağırabilir.

Öte yandan C dilinde bir programın çalışması için:

#include <stdio.h>
int topla(int n){
   if(n==1)
    return n;
   return n+topla(n-1);
}
int main(){
   topla(5);
}

Şeklinde program yazılır ve derlenir (compile) sonuçta bu program sadece 5’e kadar olan sayıları toplar. Yukarıdaki kod ise scheme dilinden farklı olarak, emirler içeren (Statements) bir yapıdadır. C dilinde her emir veya satır (statement) kural olarak noktalı virgül ile bitirilmekte ve adet olarak yeni bir satıra yazılmaktadır. Aslında bu satırlar birer komut yani emir olarak düşünülebilir. Örneğin yukarıdaki C kodunda ‘return n+topla(n-1);’ şeklinde verilen satır, tek başına anlamlıdır ve programlama diline bir komut (emir) vermektedir. Bu satıra mantıksal olarak ulaşıldığında verilen emir yerine getirilecektir.

Bu noktada, tanımımlamalı (declarative) diller ve emirli diller arasındaki 3 farktan bahsetmek yerinde olabilir:
1. Tanımlamalı diller, bir işlemin ne olduğu ile ilgilenirken, emirli diller bir işlemin nasıl yapılacağı ile ilgilenir
2. Tanımlamalı diller, yan etkilerden (side effects) arındırılmıştır, Emirli dillerde ise yan etkiler oluşabilir. Örneğin referans saydamlığı (referrential transparency) tanımlamalı dillerde varken, emirli dillerde referans matlığı (referential opacity) bulunmaktadır.
3. Tanımlamalı diller, matematiksel mantığa daha yakın dillerdir.

Örneğin scheme (veya lisp) gibi dillerin lambda-matematik (lambda calculus) yakın olduğunu söyleyebiliriz. Buna karşılık C gibi diller matematiksel mantıktan çok makine mantığına yakındır.

Referans Saydamlığı (referential transparency)
Referans saydamlığı kavramını şu örnek üzerinden anlatabiliriz. x=x+1 gibi bir satır, C dilinde x’in değerini 1 arttırmak için yazılmıştır. Burada x değeri 10 olarak satırın iki kere çalıştırıldığını hayal edelim. Bu durumda x değeri önce 11 sonra 12 olacaktır.
Bu satırı çalıştırırken, x=x+1 kod parçası yerine 11 veya 12 konulması farklı sonuçlar doğuracaktır. Buna karşılık aynı kod, bir fonksiyon olarak yazılırsa:
int arttir(int x) { return x + 1; }
şeklinde yazılan bir kodda aynı yan etki bulunmaz ve kodun her parçasında x değeri kesin olarak bilinir.

Refernas saydamlığı, verilen kod parçasındaki değişken değerinin kesin olarak ve matematiksel olarak gösterdiği (tanımlandığı) değeri ifade etmesi durumudur.

Daha net bir örnek üzerinden durumu açıklayalım.

int f(int x) { return x++; }

şeklkinde saydam bir örnek verelim. Bu örneği çalıştırmak için de aşağıdaki gibi bir kod parçası bulunsun:

int x= 5;
printf("%d",f(x) – f(x));
printf("%d",x);

yukarıdaki kodun ilk satırında x değeri tanımlanmış ve 5 atanmıştır. ikinci satırda f(x) fonksiyonu iki kere çağırılarak birbirinde çıkarılmıştır. Şimdi bu örnekte f(x)-f(x) değeri hiç hesaplanmadan sıfırdır denilebilir (a-a = 0 mantığında olduğu gibi bir değer kendisinden çıkarılırsa sıfırdır).

Buna karşılık aşağıdaki örneği ele alalım:

int k = 5;
int g(int x) { return k++; }

yine kodu çalıştırmak için aşağıdaki kod parçası verilsin.

printf("%d",g(x) – g(x));
printf("%d",k);

Soru: Acaba ilk örnekte, f(x) – f(x) sıfırdır diyebildiğimiz gibi, bu örnekte de g(x) – g(x) sıfırdır diyebilir miyiz?

Kodun çalışmasına baktığımızda, ilk g(x) çağrılmasında k değeri 5 olarak dönecek ancak hafızadaki değeri 6 olacaktır. ikinci çağırmada ise bu değer 6 dönecek ve hafızadaki değeri 7 olacaktır.

Program çalıştıktan sonra ilk printf, ekrana -1 ve ikinci printf ekrana 7 basacaktır (ilk örnekte ise 0 ve 5 basılmaktaydı).

Demek ki kodun çalışması sırasında matematiksel olarak her ne kadar g(x) – g(x) sıfır olsa da beklenmedik bir yan etki oluşabilmektedir.

Yorumlar

  1. Ogrenci

    Hocam scheme dilinde verdiğiniz örnekte fonksiyonu bir kez çağırdıktan sonra yeni bir tanımlama yapmadan kullanılamaz mı? örnek verirsek (topla 5) in ardına yeni bir komut (topla 6) şeklinde.Birde tanımlamalı programlamada daha çok nasıl sorusuna cevap verecek komutlar olması gerekmez mi , mesela C dilinde yazılan bir kodda stack patlayabilir bu yüzden nasıl sorusuna da cevap vermesi gerekir,haskell dili ise %100 free bug programlar yazılabildiğini biliyorum bunu yapabilmeside dilin nasıl sorusuyla ilgilenmeden direk ne sorusuna yönelik kodlanmasına dayalı olduğunu düşünüyorum.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir