Rekursiyalar

   Biz artıq bilirik ki, bir funksiya öz daxilində başqa funksiyanı çağıra bilər. Amma bilməmiz gərəkən daha bir şey var: funksiya özü özünü də çağıra bilər. Proqramlaşdırmada funksiyaların özü özünü çağırmasına rekursiya, bu cür funksiyalara da rekursiv funksiyalar deyilir.

   Gəlin verilmiş ədədin faktoriyalını (n!) hesablayan kiçik bir funksiya hazırlayaq. Riyaziyyatdan da bildiyiniz kimi n ədədinin faktorialı 1-dən n-ə qədər (daxil olmaqla) natural ədədlərin hasilinə bərabərdir. Məsələn, 5!=5*4*3*2*1. Və o da məlumdur ki, 1!=1 və 0!=1. Buradan belə bir məntiq yürütmək olur ki, n! hesablamaq üçün n*(n-1)! hesablamalıyıq, yəni 5!=5*4!. Belə olan halda 4! üçün 4*3!, 3! üçün 3*2!, 2! hesablamaq üçün də 2*1! hesablanmalıdır. 1! isə 1-ə bərabər olduğuna görə rekursiya burada bitmiş olur.

   O zaman rekursiv funksiya aşağıdakı kimi olacaq:

>>> def fakt(n):
        if n<=1:
                return 1
        else:
                return n*fakt(n-1)


>>> fakt(5)
120
>>>

   Rekursiv funksiyalardan istifadə edərkən diqqət etmək lazımdır ki, sonsuz rekursiya alınmasın. Adətən sonsuz rekursiyalara gətirib çıxaran aşağıdakı iki səbəb olur:

  1. Rekursiyadan çıxışın düzgün təşkil edilməməsi. Məsələn, yuxarıdakı faktorial funksiyasında if n<=1 şərti verilməsəydi, rekursiya mənfi ədədlərə doğru sonsuz şəkildə davam edərdi.
  2. Rekursiv çağırışda parametrlərin düzgün verilməməsi. Məsələn, əgər fakt(n) funksiyası fakt(n-1) əvəzinə elə fakt(n) funksiyasını çağırsaydı, yenə də sonsuz rekursiya alınardı.

   Gəlin daha bir misal işləyək. Verilmiş a ədədini n qüvvətinə yüksəldən funksiya təyin edək:

>>> def power(a,n):
        if n==1:
                return a
        else:
                return a*power(a,n-1)


>>> power(2,4)
16
>>> power(8,3)
512
>>>

 Biz ən başda qeyd etdik ki, rekursiv funksiyalar özü özünə müraciət edən funksiyalardır. Amma bu müraciətlərin sayı (rekursiyanın dərinliyi) sonsuz ola bilməz. Hətta səhvən sonsuz rekursiya təşkil etsək belə praktiki olaraq funksiya sonsuz sayda icra olunmayacaq. Rekursiyanın dərinliyi ilə bağlı aşağıdakıları bilmək vacibdir:

  1. Rekursiyanın dərinliyinə məhdudiyyət qoyulmuşdur. Default olaraq (susmaya görə) rekursiv funksiya 1000 dəfə özü özünə müraciət edə bilər. 
  2. Bu məhdudiyyət sys.setrecursionlimit() funksiyasından istifadə edilməklə dəyişdirilə bilər. Cari limitə baxmaq üçün isə sys.getrecursionlimit() funksiyası çağırılmalıdır.
  3. Buna baxmayaraq rekursiyanın dərinliyi əməliyyat sistemi tərəfindən təyin olunan stekin ölçüsü ilə məhdudlaşdırılır.

  Son olaraq onu da deyək ki, rekursiv funksiyalar ümumilikdə proqramın məhsuldarlığını azaltdığı üçün onlara yalnız zərurət halında müraciət etmək lazımdır.

Məntiq operatorları

   Yuxarıda qeyd etmişdik ki, nəticəsi doğru (True) və ya yalan (False) olan ifadələr məntiqi ifadələrdir. Biz sadə məntiqi ifadələrdən xüsusi məntiqi birləşdiricilərin köməyilə daha mürəkkəb məntiqi ifadələr əldə edə bilərik. Bu məntiqi birləşdiricilərə məntiq operatorları deyilir. 

   Python proqramlaşdırma dilində məntiq operatorları kimi and (və), or (və ya) və not (deyil) operatorlarındn istifadə olunur. 

   Sadə məntiqi ifadələrin məntiq tipində nəticəsi (qiyməti) olduğu kimi, mürəkkəb məntiqi ifadələrin də məntiqi nəticəsi (qiyməti) vardır. Mürəkkəb məntiqi ifadələrin nəticəsi onu təşkil edən sadə məntiqi ifadələrin nəticələrindən (qiymətlərindən) və bu ifadələri birləşdirən məntiqi birləşdiricilərin növündən, yəni istifadə olunan məntiq operatorundan asılıdır. 

  Gəlin bu dediklərimizi aşağıdakı cədvəllərdə göstərək:

ifadə1 ifadə2 ifadə1  and  ifadə2  ifadə1  or  ifadə2
 False  False  False  False
 False  True  False  True
 True  False  False  True
 True  True  True  True
ifadə not  ifadə
 False  True
 True  False

   Riyaziyyatda bu cədvəllərə doğruluq cədvəlləri deyilir. Birinci cədvəldən də gördüyünüz kimi ifadə1 and ifadə2 mürəkkəb məntiqi ifadəsinin qiyməti yalnız o halda True (doğru) olur ki, onu təşkil edən ifadə1ifadə2 eyni zamanda True qiymətini almış olsun, qalan digər hallarda  isə mürəkkəb məntiqi ifadənin qiyməti False (yalan) olur. 

   Yenə birinci cədvəldən aydın olur ki, ifadə1 or ifadə2 mürəkkəb məntiqi ifadəsinin qiyməti o halda True (doğru) olur ki, onu təşkil edən ifadə1 ifadə2 ifadələrindən heç olmazsa biri True qiymətini almış olsun, qalan yeganə halda (hər ikisi False olduqda)  isə mürəkkəb məntiqi ifadənin qiyməti False (yalan) olur.

   İkinci cədvəldə göstərilən not (məntiqi inkar, deyil) operatoru verilmiş məntiqi ifadənin inkarını əldə etməyə imkan verir. Əgər ifade qiymət olaraq True almışsa, o zaman not ifade bizə False qiymətini verəcək. Eyni qayda ilə əgər ifade qiymət olaraq False almışsa, bu zaman da not ifade bizə True qiymətini verəcək. Burad diqqət yetirsəniz görərsiniz ki, not operatoru yalnız bir ifadə ilə işləyir. Belə operatorlara unar operator deyilir. 

   İndi isə məntiq operatorlarını misallar üzərində nəzərdən keçirək:

>>> a=5
>>> b=10
>>> c=2.5
>>> p="Bakı"
>>> a>3 and b<=15 True >>> p=="Bakı" or p=="bakı"
True
>>> not c==2.5
False
>>>

   Yuxarıdakı misallara bənzər sadə məntiqi ifadələrdən müxtəlif kombinasiyalı mürəkkəb məntiqi ifadələr qurun və proqramını yazaraq sınaqdan keçirin. Alınan nəticələri doğruluq cədvəlləri ilə yoxlayın.

Üzvlük operatorları

   Bir qiymət və ya dəyişənin verilmiş ardıcıllıq (sətir, siyahı və kortej) və ya kolleksiyanın (çoxluq və lüğət) üzvü olub-olmadığını yoxlamaq üçün üzvlük operatorlarından istifadə olunur. Python proqramlaşdırma dilində iki üzvlük operatoru mövcuddur: in (daxildir) və not in (daxil deyil):

Gəlin aşağıdakı misalları nəzərdən keçirək:

>>> s = "Dərbənd"
>>> z= [1, 2, 3]
>>> k= ('a', 'b', 'c', 'd', 'e')
>>> f = "bənd"
>>> n = 4
>>> t = 'g'
>>> f in s
True
>>> n in z
False
>>> t not in k
True
>>>

   Gördüyünüz kimi, əgər dəyişənin aldığı qiymət ardıcıllıqda tapılırsa ifadənin nəticəsi True, əks halda False olur.

Pünhan babanın yaşı

   Havası təmiz, günəşi bol olan bir şəhərdə 13 yaşlı Adil yaşayır. Adilin xətrini dünyalar qədər istədiyi Pünhan adlı babası var. Günlərin bir günü Adil babasından yaşını soruşur. Pünhan baba deyir lki, mənim yaşım 40-dan az deyil. Amma sənə yaşımı deməyəcəm, gərək özün tapasan. Bununla belə sənə bir ipucu verə bilərəm.

Punhan Baba

   Mənim yaşım natural bir ədəddir. Amma cüt və ya tək olduğunu da deməyəcəm. Əgər yaşım cütdürsə, onu 2-yə böl, təkdirsə ona 1 əlavə et. Əldə etdiyin nəticə üzərində yenə eyni əməliyyatları icra elə, ta ki 1 alınana qədər. Yerinə yetirdiyin bu əməliyyatların sayı sənin yaşına bərabər olacaq. Artıq mənim yaşımı tapa bilərsən.

   Pünhan baba həm də Adilə söz verdi ki, əgər yaşını tapa bilsə, ona maşın sürməyi öyrədəcək. Adilə babasının yaşını tapmaqda kömək edin.

Giriş verilənlərinin formatı

   Tək sətirdə Adilin yaşı olan 13 ədədi verilir.

Çıxış verilənlərinin formatı

   Pünhan babanın yaşını tək sətirdə çap etmək lazımdır.

   Proqramı özünüz tərtib etməyə çalışın. Əgər alınmasa, aşağıdakı həll ilə tanış olun.

Punhan_Baba

İdentiklik operatorları

   Bildiyiniz kimi, qiymət və dəyişənlərin bərabər olub-olmadığını yoxlamaq üçün bərabərlik (==!=) operatorlarından istifadə olunur. Bundan başqa Python dilində obyektlərin identikliyini yoxlayan identiklik (isis not) operatorları da nəzərdə tutulmuşdur. Burada xüsusilə qeyd etmək lazımdır ki, bu iki anlayış (bərabərlikidentiklik) bir-birindən ciddi mənada fərqlənir. 

Gəlin aşağıdakı misalları nəzərdən keçirək:

>>> L1 = [1, 2, 3]
>>> L2 = [1, 2, 3]
>>> L3 = L1
>>> L1 == L2
True
>>> L1 is L2
False
>>> L1 is L3
True
>>> L2 is not L3
True
>>>

   Yuxarıdakı proqramda L1, L2L3 dəyişənlərinə siyahı (list) tipində qiymətlər mənimsədilir. Burada L1L2 dəyişənləri eyni məzmunda olan, lakin fərqli siyahılara istinad edirlər. Başqa sözlə desək, istinad edilən siyahılar ayrı-ayrı obyektlərdir və yaddaşın müxtəlif ünvanlarında yerləşirlər. Və məhz buna görə də, onlar bərabər olsa da identik sayılmırlar. Proqramda həm də L3 dəyişəninə L1 siyahısı mənimsədilir. Bu zaman L3 dəyişəni də L1 dəyişəninin istinad etdiyi eyni obyektə istinad edəcək. Bu isə onların identik olması mənasına gələcək. Və sonda L2L3 siyahılarının da identik olmadığı göstərilir.

Daha bir neçə misalı nəzərdən keçirək:

>>> a = 123
>>> b = 123
>>> s1 = 'salam'
>>> s2 = 'salam'
>>> a is b
True
>>> s1 is s2
True
>>>

   Bu proqramda biz ab dəyişənlərinə tam ədəd tipində 123 qiymətini, s1s2 dəyişənlərinə də sətir tipində ‘salam’ qiymətini mənimsətdik. Məntiqlə ab, eləcə də s1s2 fərqli obyektlərə istinad etdiklərinə görə öz aralarında identik olmamalı idilər. Lakin bu dəyişənlərə identiklik operatorunu tətbiq etdiyimiz zaman bunun əksini görürük. Əcəba niyə? Məsələ burasındadır ki,  fərqli dəyişənlərə eyni kiçik ədədləri və qısa sətirləri mənimsətdiyimiz zaman Python dilinin interpretatoru optimallaşdırma məqsədilə keşləmə həyata keçirir və bu dəyişənləri eyni obyektə bağlayır. Lakin əgər daha böyük ədədlər və daha uzun sətirlər istifadə olunarsa, bu zaman vəziyyət dəyişər və dəyişənlər fərqli obyektlərə istinad etmiş olar:

>>> a = 12345
>>> b = 12345
>>> a is b
False
>>> s1= 'salam dostlar'
>>> s2= 'salam dostlar'
>>> s1 is s2
False
>>>

   Proqramdan da gördüyünüz kimi ab, eləcə də s1s2 dəyişənləri artıq öz aralarında identik deyillər.

Müqayisə operatorları

   Proqramlaşdırmada bir çox halda qiymətləri, eləcə də dəyişənləri bir-birilə müqayisə etmək lazım gəlir. Bunun üçün hər bir proqramlaşdırma dilində olduğu kimi Python dilində də müqayisə operatorları nəzərdə tutulmuşdur:

Operator Təyinatı
< kiçikdir (a<b) 
> böyükdür (a>b) 
 == bərabərdir (a==b)
<= kiçik bərabərdir (a<=b) 
>= böyük bərabərdir (a>=b)
!=   <> bərabər deyil (a!=b və ya a<>b)

   Cədvəldə bərabərsizliyi müqayisə edən iki operator (!=<>) göstərilmişdir. Qeyd etmək lazımdır ki, yeni versiyada != operatorundan istifadə edilir. Sizin də bu variantdan istifadə etməyiniz tövsiyə olunur.

round() funksiyası

  round() funksiyası verilmiş ədədi onluq vergüldən sonra göstərilmiş mərtəbəyə qədər yuvarlaqlaşdıraraq qaytarır.

Funksiyanın sintaksisi aşağıdakı kimidir:

round(number, ndigits)

   Burada number verilmiş həqiqi ədəd, ndigits isə onluq vergüldən sonra göstərilən mərtəbələrin sayıdır. İkinci parametr olan ndigits istəyə bağlı olaraq istifadə edilir, boş buraxıldıqda, yuvarlaqlaşdırma ən yaxın tam ədədə qədər aparılır. Əgər round() funksiyasına number olaraq tam ədəd verilsə, funksiya tam ədəd qaytaracaq.

Aşağıda round() funksiyasının istifadəsi ilə bağlı nümunələr verilmişdir:

>>> print(round(10.7))
11
>>> print(round(5.3))
5
>>> print(round(-5.7))
-6
>>> print(round(8))
8
>>>
>>> print(round(1.5))
2
>>> print(round(2.5))
2
>>>

   Yuxarıdakı misallardan sonuncusunun nəticəsi Sizi çaşdıra bilər. Deyəcəksiniz ki, axı nəticə 3 olmalıydı. Çünki riyaziyyatdan da bildiyimiz kimi hansı mərtəbəyə qədər yuvarlaqlaşdırılma tələb edilirsə, həmin mərtəbədəki rəqəmdən sağdakı rəqəm 4-dən böyükdürsə, qeyd olunan rəqəm bir vahid artırılır, əks halda isə rəqəm dəyişmir, bu rəqəmin sağındakı bütün rəqəmlər isə sıfırlarla əvəz edilir. Amma məsələ burasındadır ki, Python dilinin round() funksiyası riyazi deyil, bank yuvarlaqlaşdırması edir, yəni yuvarlaqlaşma ən yaxın cüt ədədə qədər aparılır.

   Aşağıdakı misallarda ndigits parametri istifadə edilir:

>>> print(round(2.56, 1))
2.6
>>> print(round(3.874, 2))
3.87
>>> print(round(2.665, 2))
2.67
>>> print(round(2.675, 2))
2.67
>>>

   Yenə də sonuncu misaldakı nəticə sürpriz oldu. Gözləyirdiniz ki, round(2.675, 2) funksiyası 2.68 verəcək, amma 2.67 verdi. Funksiyanın bu davranışı xəta deyil. Məsələ burasındadır ki, bir çox onluq kəsrləri dəqiqliklə həqiqi ədədlər kimi təsvir etmək mümkün deyil. Məsələn 2.675 onluq ədədi ikili sürüşkən vergüllü ədədə çevirdikdə, o ikili yaxınlaşma ilə əvəz olunur və dəqiq qiyməti aşağıdakı kimi olur:

2.67499999999999982236431605997495353221893310546875

   Məhz buna görə də yuvarlaqlaşdırmanın nəticəsi 2.67 olur.

   Dəqiqlik tələb olunan hallarda isə sürüşkən ədədlərlə işləmək üçün nəzərdə tutulan decimal modulundan istifadə etmək olar:

>>> from decimal import Decimal
>>> num = Decimal('2.675')
>>> print(round(num, 2))
2.68
>>>