Agregat funksiyalar, Oracle SQL-də bir və ya bir neçə sətirdən ibarət məlumatları analiz etmək və ümumi nəticə çıxarmaq üçün istifadə olunur. Bu funksiyalar cədvəllərdəki dəyərləri qruplaşdırmaq, hesablamaq və filtrasiya etmək imkanı verir.
Aşağıdakı ən çox istifadə olunan agregat funksiyalarla tanış olaq:
İlk öncə misalları rahat göstərmək üçün employees adından cədvəl yaradaq və məlumatları ora əlavə edək
Bu funksiya müəyyən bir sütundakı və ya bütün cədvəldəki sətirlərin sayını verir.
Sintaksis:
SELECT COUNT(*) FROM employees; --4
Bütün işçilərin sayını qaytarır.
Sütun səviyyəsində:
SELECT COUNT(department_id) FROM employees; --3
NULL olmayan department_id sütunlarının sayını qaytarır.
SUM() – Toplam hesablayır
Bu funksiya ədədi dəyərləri toplayır.
SELECT SUM(salary) FROM employees; --9600
Bütün işçilərin ümumi maaşını qaytarır.
AVG() – Orta dəyəri hesablayır
Bu funksiya ədədi sütun üzrə orta (average) dəyəri verir.
SELECT AVG(salary) FROM employees; --2400
Bütün işçilərin orta maaşını göstərir.
MIN() – Ən kiçik dəyəri tapır
Ən aşağı ədədi və ya əlifba sıralamasına görə minimum dəyəri qaytarır.
SELECT MIN(salary) FROM employees; --1600
Ən az maaşı olan işçinin maaşını qaytarır.
SELECT MIN(name) FROM employees; --Emily Davis
Əlifba sırasına görə ilk gələn adı göstərir.
MAX() – Ən böyük dəyəri tapır
Ən yüksək dəyəri qaytarır.
SELECT MAX(salary) FROM employees; --3500
Ən yüksək maaşı göstərir.
GROUP BY ilə agregat funksiyalar
Agregat funksiyaları GROUP BY ilə birlikdə istifadə edərək məlumatları qruplar üzrə analiz etmək mümkündür.
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id;
Hər şöbə üçün orta maaş göstərilir.
HAVING ilə agregat filtr
Agregat nəticələri filtrasiya etmək üçün HAVING istifadə olunur. WHERE fərdi sətirlər üçün, HAVING isə GROUP BY nəticələri üçün tətbiq edilir.
SELECT department_id, COUNT(*)
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 5;
Yalnız 5-dən çox işçisi olan şöbələri göstərir.
Nəticə
Oracle SQL-də agregat funksiyalar məlumatların xülasəsini çıxarmaq, ümumi mənzərəni görmək və qruplar üzrə analiz aparmaq üçün çox faydalıdır. GROUP BY, HAVING kimi əmr və funksiyalarla birlikdə istifadə olunduqda, çox güclü hesabatlar və analizlər aparmaq mümkündür.
Verilənlər bazasında məlumatlar adətən normalizasiya edilmiş, yəni müxtəlif cədvəllərdə saxlanılır. Bu cədvəlləri əlaqələndirmək üçün JOIN əməliyyatlarından istifadə olunur. Oracle SQL-də bir neçə növ JOIN mövcuddur və hər biri fərqli ehtiyaclara cavab verir.
1. INNER JOIN – Daxili birləşdirmə
INNER JOIN iki və ya daha çox cədvəldə yalnız uyğun gələn (ortaq açar dəyəri olan) sətirləri birləşdirir. Nəzəri olaraq, inner join SQL sorğusu iki cədvəl arasında müqayisə edilən sətrlərin hamısının eyni dəyərlərə malik olması tələb olunur. Nəticədə, yalnız uyğunluq olan sətrləri nəticəyə əlavə edir.
Nümunə üçün iki cədvəli düşünək:
Cədvəl 1: sifarişlər (orders)
order_id
customer_id
goods_name
order_date
101
1
Laptop
2024-05-01
102
1
Telefon
2024-05-03
103
2
Tablet
2024-05-02
104
3
Kamera
2024-05-04
105
3
Klaviatura
2024-05-05
Cədvəl 2: müştərilər (customers)
customer_id
name
surname
1
Ali
Məmmədov
2
Nigar
Əliyeva
3
Cavid
Məmmədli
Bu iki cədvəli birləşdirmək üçün aşağıdakı SQL sorğusunu istifadə edə bilərik:
SELECT customers.name, customers.surname, orders.goods_name, orders.order_date
FROM orders
INNER JOIN customers ON customers.customer_id = orders.customer_id;
Bu sorğunun nəticəsi belə olacaq:
name
surname
goods_name
order_date
Ali
Məmmədov
Laptop
2024-05-01
Ali
Məmmədov
Telefon
2024-05-03
Nigar
Əliyeva
Tablet
2024-05-02
Cavid
Məmmədli
Kamera
2024-05-04
Cavid
Məmmədli
Klaviatura
2024-05-05
Bu sorğu “müştərilər” və “sifarişlər” cədvəllərini customer_id sütunu əsasında birləşdirir. Nəticədə, hər bir sifarişin hansı müştəri tərəfindən verildiyi və sifariş tarixi ilə birlikdə müştəri adını və soyadını göstərir.
-- müştərilərilər cədvəlinin yaradılması
CREATE TABLE customers (
customer_id NUMBER PRIMARY KEY,
name VARCHAR2(50),
surname VARCHAR2(50)
);
-- müştərilər cədvəlinin doldurulması
INSERT INTO customers (customer_id, name, surname) VALUES
(1, 'Ali', 'Məmmədov'),
(2, 'Nigar', 'Əliyeva'),
(3, 'Cavid', 'Məmmədli');
-- sifarişlər cədvəlinin yaradılması
CREATE TABLE orders (
order_id NUMBER PRIMARY KEY,
customer_id NUMBER,
goods_name VARCHAR2(100),
order_date DATE,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
-- sifarişlər cədvəlinin doldurulması
INSERT INTO orders (order_id, customer_id, goods_name, order_date) VALUES
(101, 1, 'Laptop', DATE '2024-05-01'),
(102, 1, 'Telefon', DATE '2024-05-03'),
(103, 2, 'Tablet', DATE '2024-05-02'),
(104, 3, 'Kamera', DATE '2024-05-04'),
(105, 3, 'Klaviatura', DATE '2024-05-05');
2. LEFT JOIN – Sol birləşdirmə
LEFT JOIN sol cədvəldəki bütün sətirləri, sağ cədvəldə isə yalnız uyğun gələnləri birləşdirir. Əgər sağ tərəfdə uyğun sətir yoxdursa, həmin sütunlarda NULL göstərilir.
İki cədvəl nümunəsi:
employees cədvəli:
employee_id
name
department_id
1
John Smith
10
2
Jane Doe
NULL
3
Emily Davis
20
4
Michael Brown
30
departments cədvəli:
department_id
department_name
10
IT
20
HR
30
Finance
SELECT employees.employee_id, employees.name, departments.department_name
FROM employees
LEFT JOIN departments
ON employees.department_id = departments.department_id;
RIGHT JOIN sağ cədvəldəki bütün sətirləri, sol cədvəldə isə uyğun gələnləri birləşdirir. Əgər sol cədvəldə uyğun gələn sətir yoxdursa, onda nəticə dəstində sol cədvəldəki sütunlar üçün NULL dəyərləri olacaq.
Bir nümunə ilə izah edək. İki cədvəlimiz var:
Orders (Sifarişlər)
Customers (Müştərilər)
Orders cədvəli:
OrderID
CustomerID
OrderDate
1
1001
2023-01-15
2
1002
2023-02-20
3
1003
2023-03-12
Customers cədvəli:
CustomerID
CustomerName
1001
Ali
1002
Ayşe
1004
Mehmet
RIGHT JOIN sorğusu ilə Orders və Customers cədvəllərini birləşdirək:
SELECT Orders.OrderID, Orders.OrderDate, Customers.CustomerName
FROM Orders
RIGHT JOIN Customers ON Orders.CustomerID = Customers.CustomerID;
Bu sorğunun nəticəsi belə olacaq:
OrderID
OrderDate
CustomerName
1
2023-01-15
Ali
2
2023-02-20
Ayşe
NULL
NULL
Mehmet
Göründüyü kimi, Customers cədvəlindəki bütün müştərilər nəticədə göstərilir, hətta Orders
cədvəlində uyğun gələn sifariş olmadıqda belə. Mehmet üçün sifariş məlumatı olmadığından, onun üçün NULL dəyərləri qaytarılır.
🧬 Object.create() vasitəsilə İrsi əlaqə (Inheritance)
JavaScript-də irsi əlaqə qurmağın başqa bir güclü və sadə yolu Object.create() metodudur. Bu metod vasitəsilə yeni bir obyekt yaradılır və bu obyektin prototipi kimi istədiyimiz digər bir obyekt təyin edilir.
Bu üsul klassik constructor-lar və prototype zənciri qurmaqdan daha sadə və daha oxunaqlıdır.
rabbit obyektini Object.create(animal) ilə yaratmışıq.
rabbit obyektində eats xüsusiyyəti yoxdur, lakin animal onun prototipidir, ona görə həmin xüsusiyyətə çatmaq mümkündür.
Bu, prototip zəncirinin bir hissəsidir və miras alınmış metod və xüsusiyyətlər animal obyektindən gəlir.
Object.create() istifadəsinin üstünlükləri:
Daha sadə sintaksis.
Klassik constructor funksiyalarına ehtiyac olmadan obyekt əsaslı irsilik qurmaq imkanı.
Müxtəlif obyektlər arasında açıq şəkildə əlaqə yaratmaq olur.
Test və prototipləşdirmə üçün çox uyğundur və çevikdir.
Beləliklə, Object.create() JavaScript-də irsi əlaqənin daha bir güclü vasitəsidir və Prototype Chain anlayışının birbaşa tətbiqini nümayiş etdirir. Bu metod xüsusilə funksional proqramlaşdırma yanaşmalarında və modul sistemlərində tez-tez istifadə olunur.
JavaScript obyekt yönümlü proqramlaşdırma (OOP) yanaşmasını prototip əsaslı sistem vasitəsilə həyata keçirir. Bu, digər obyektlərin xüsusiyyətlərini miras almaq üçün klassik classlardan istifadə etmədən, obyektlər arasında birbaşa əlaqə qurulmasına imkan verir. Bu prosesə Prototype Chain, yəni Prototip Zənciri deyilir.
Prototip nədir?
JavaScript-də hər bir obyektin arxasında [[Prototype]] adlı gizli bir xüsusiyyət var. Bu xüsusiyyət digər obyektə (və ya null-a) istinad edir. Əgər siz bir obyektin içində müəyyən xüsusiyyət və ya metod axtarırsınızsa, JavaScript əvvəlcə həmin obyektin özündə yoxlayır, əgər tapa bilmirsə, onun prototipində axtarmağa davam edir. Bu proses Prototip Zəncirini təşkil edir.
Prototip zəncirinin iş prinsipi
Tutaq ki, bir obyektin içində sayHello() adlı metod yoxdur. JavaScript bu metodu həmin obyektin [[Prototype]]-ində axtarır. Əgər orada da tapa bilmirsə, növbəti prototipə keçir və bu proses null-a qədər davam edir.
Burada rabbit → animal → creature zənciri qurulub. rabbit.breathes çağırışı 3 səviyyəli zəncir vasitəsilə true cavabı verir.
Prototiplər və Constructor funksiyalar
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("Salam, mənim adım " + this.name);
};
const user = new Person("Elvin");
user.sayHello(); // Salam, mənim adım Elvin
Burada user obyektinin sayHello metodu özündə yoxdur. JavaScript onun konstruktorunun (Person) prototype obyektinə baxır və oradan tapır.
🧬 Object.prototype və Zəncirin sonu
JavaScript-də bütün obyektlər nəticədə Object.prototype-dən miras alır.
Oracle SQL verilənlər bazası ilə işləyənlər üçün əsas əmrlərdən bəziləri cədvəl yaratmaq, cədvələ məlumat əlavə etmək və məlumatları sorğulamaqdır. Bu əmrlər vasitəsilə siz öz bazanızı qurur, məlumatları saxlayır və onlara çıxış əldə edirsiniz.
🧱 1. CREATE TABLE – Yeni cədvəl yaratmaq
Oracle SQL-də yeni bir cədvəl yaratmaq üçün CREATE TABLE əmri istifadə olunur.
Proqramlaşdırma paradigmaları — proqramların necə yazılacağını və necə qurulacağını müəyyən edən yanaşmalar və modellər toplusudur. Hər bir paradigma öz qaydaları, düşüncə tərzi və kod yazma üsulları ilə fərqlənir. Paradigmalar proqramçılara daha səliqəli, çevik və başa düşülən kod yazmaqda kömək edir.
Bu anlayışı daha sadə şəkildə ifadə etsək, proqramlaşdırma paradigmaları – “problemi necə həll etməliyik?” sualına verilən müxtəlif cavablardır.
Əsas proqramlaşdırma paradigmaları
1. İmperativ paradigma
İmperativ proqramlaşdırma, kompüterə “nə etməli” olduğunu deyil, “necə etməli” olduğunu deməkdir. Bu yanaşmada proqramçı ardıcıl əmrlərlə kompüterin nə edəcəyini təyin edir.
Xüsusiyyətləri:
Dəyişənlərdən istifadə olunur
Addım-addım təlimat verilir
Nəticə ardıcıl əməliyyatlarla əldə edilir
Məşhur dillər: C, Python, JavaScript
Misal:
let total = 0;
for (let i = 1; i <= 5; i++) {
total += i;
}
console.log(total); // 15
2. Deklarativ paradigma
Deklarativ proqramlaşdırma nə etmək istədiyini deyir, necə etmək lazım olduğunu isə sistem özü qərar verir. Yəni məqsədi göstəririk, amma yolu yox.
Məşhur dillər və texnologiyalar: SQL, HTML, React (JSX)
Bu paradigma obyektlər üzərində qurulub. Obyekt — bir varlığı (nəsnəni) təmsil edir və bu varlığın xüsusiyyətləri (property) və funksiyaları (method) olur.
Əsas prinsiplər:
Enkapsulyasiya (Encapsulation)
İrsi əlaqə (Inheritance)
Polimorfizm (Polymorphism)
Abstraksiya (Abstraction)
Məşhur dillər: Java, C++, Python, JavaScript (ES6+)
Misal:
class Animal {
speak() {
console.log("Animal sound");
}
}
class Dog extends Animal {
speak() {
console.log("Woof!");
}
}
const dog = new Dog();
dog.speak(); // Woof!
4. Funksional paradigma
Funksional proqramlaşdırma təmiz funksiyalara əsaslanır. Bu yanaşma dəyişənlərin dəyişdirilməməsi (immutability) və funksiyaların birinci səviyyə obyektlər kimi istifadə olunması prinsiplərinə əsaslanır.
Xüsusiyyətləri:
Təmiz funksiyalar (pure functions)
Yan təsirsiz (no side effects)
Dəyişməzlik (immutability)
Məşhur dillər: Haskell, Lisp, Elm, JavaScript (funksional üslub)
Çoxlu proqramlaşdırma dilləri birdən çox paradigmanı dəstəkləyir. Məsələn, Python, JavaScript və C# həm obyekt-yönlü, həm də funksional yanaşmaları dəstəkləyir.
Paradigmanın seçilməsi
Proqramlaşdırma paradigması seçərkən aşağıdakıları nəzərə almaq faydalıdır:
Problem növü və miqyası
Komanda bacarıqları
Dilin imkanları
Performans və oxunaqlılıq
Nəticə
Proqramlaşdırma paradigmaları, bir problemi necə modelləşdirəcəyimizi və həll edəcəyimizi formalaşdıran çərçivələrdir. Hər bir yanaşmanın öz üstünlükləri və zəif cəhətləri var. Müasir proqramçılar bir neçə paradigmaya bələd olmalı və uyğun vəziyyətdə düzgün yanaşmanı seçməyi bacarmalıdır.
JavaScript-də funksiyalar obyektlər kimi qəbul olunur. Bu səbəbdən funksiyalara aid əlavə metodlardan istifadə etmək mümkündür. Bu metodlardan ən çox istifadə olunanları bind(), call() və apply() funksiyalarıdır. Bu metodlar vasitəsilə funksiyanın this kontekstini istədiyimiz şəkildə dəyişə bilərik.
Bu məqalədə hər üçünü sadə və aydın şəkildə izah edəcəyik.
1. call() Metodu
Təyinat:
call() funksiyası bir funksiyanı başqa bir obyektin konteksti (this) ilə çağırmağa imkan verir.
Sintaksis:
funksiya.call(thisArg, arg1, arg2, ...)
thisArg – funksiyanın içində this olaraq istifadə ediləcək obyekt.
const user = {
ad: 'Nilay',
salamla: function(yas) {
console.log(`Salam, mənim adım ${this.ad} və mən ${yas} yaşındayam.`);
}
};
const digerUser = {
ad: 'Elvin'
};
user.salamla.call(digerUser, 30);
// Çıxış: Salam, mənim adım Elvin və mən 30 yaşındayam.
apply() funksiyası call() metoduna bənzəyir, yalnız arqumentlər massiv (array) şəklində verilir.
Sintaksis:
funksiya.apply(thisArg, [arg1, arg2, ...])
Nümunə:
function topla(a, b) {
return a + b;
}
console.log(topla.apply(null, [5, 10])); // 15
İstifadə Fərqi:
call() → arqumentləri ayrı-ayrı ötürürük.
apply() → arqumentləri massiv kimi ötürürük.
3. bind() Metodu
Təyinat:
bind() funksiyası call() və apply() kimi this kontekstini dəyişmək üçün istifadə olunur, lakin funksiyanı dərhal çağırmır. Əvəzində, yeni bir funksiyanı qaytarır.
const user = {
ad: 'Nilay'
};
function salamla(yas) {
console.log(`Salam, mənim adım ${this.ad} və mən ${yas} yaşındayam.`);
}
const salamlaNilay = salamla.bind(user, 25);
salamlaNilay(); // Salam, mənim adım Nilay və mən 25 yaşındayam.
const user = {
ad: 'Nilay',
salamla: function() {
console.log(`Salam, mənim adım ${this.ad}`);
}
};
setTimeout(user.salamla, 1000); // undefined (çünki this itirilib)
setTimeout(user.salamla.bind(user), 1000); // Salam, mənim adım Nilay
Nəticə
bind(), call() və apply() JavaScript funksiyalarında this kontekstini idarə etmək üçün əsas vasitələrdir. Onların fərqlərini və istifadələrini bilmək, daha təmiz və anlaşılan kod yazmağınıza kömək edəcək.
call() və apply() dərhal funksiyanı çağırır.
bind() isə yeni bir funksiya qaytarır və istənilən vaxt çağırmaq mümkündür.
Bu metodlar — funksiya davranışına nəzarət etmək və təkrar istifadəni artırmaq üçün proqramçının alətlər çantasındakı vacib alətlərdəndir.
Closure JavaScript-də çox vacib və güclü bir anlayışdır. Bu anlayışı bilmək funksiyaların necə işlədiyini daha dərindən anlamağa kömək edir və bir çox qabaqcıl texnikaların əsasını təşkil edir.
Sadə dillə desək, closure — bir funksiyanın özünün daxilində yaradıldığı mühitə (scope) çıxış əldə etməsi deməkdir, hətta o mühit artıq mövcud olmasa belə.
Closure necə işləyir?
JavaScript-də bir funksiya başqa bir funksiyanın içində yaradıldıqda, daxili funksiya xarici funksiyanın dəyişənlərinə çıxış edə bilir. Bu əlaqə closure adlanır.
Başqa sözlə:
Daxili funksiya (inner function) xarici funksiyanın (outer function) dəyişənlərini “yada saxlayır” və istifadə edə bilir.
multiplier(2) çağırışında x=2 dəyəri yadda qalır və iki dəfə artıran funksiya yaranır.
Closure xüsusiyyətləri
Xüsusiyyət
Təsviri
Mühit
Closure funksiyanın yaradıldığı mühiti saxlayır
Uzunömürlülük
Closure-lar lazımi dəyişənləri yadda saxlayır, mühit bağlansa da işləyir
Özəl dəyişənlər
Closure ilə xaricdən dəyişdirilə bilməyən dəyişənlər yaradıla bilər
Nəticə
Closure JavaScript proqramlaşdırmasında çox güclü bir anlayışdır. Bu konsepti başa düşmək kodunuzu daha güclü, çevik və təhlükəsiz edəcəkdir. Closure-ları başa düşən proqramçı asinxron əməliyyatları, modul dizaynını və kompleks funksiyaları daha rahat idarə edə bilər.
Unutmayın: Closure — funksiyanın yadda saxladığı gizli bağdır.
Hoisting JavaScript-də olduqca vacib və bəzən çaşdırıcı bir anlayışdır. Bu anlayışı düzgün başa düşmək səhvlərin qarşısını almağa kömək edir və kodun necə işlədiyini daha yaxşı anlamağımıza səbəb olur.
Sadə dillə desək, hoisting o deməkdir ki, bütün dəyişənlərin və funksiyaların tanımlamaları (declarations) avtomatik olaraq kodun başlanğıcına “qaldırılır”.
Yəni, siz dəyişən və ya funksiyanı kodda sonradan yazsanız belə, JavaScript onu icra etməzdən əvvəl başa keçirir və hazır vəziyyətə gətirir.
Dəyişənlərdə hoisting
JavaScript-də var, let və const ilə elan edilmiş dəyişənlər fərqli şəkildə hoisting olur.
1. var ilə hoisting
var ilə elan edilən dəyişənlər tam şəkildə undefined dəyəri ilə yuxarıya qaldırılır.
Nümunə:
console.log(a); // undefined
var a = 5;
console.log(a); // 5
İzah:
JavaScript əvvəlcə var a; sətrini avtomatik olaraq kodun əvvəlinə gətirir.
Ona görə də birinci console.log(a); zamanı a dəyişəni mövcuddur, amma dəyəri undefined olur.
Əslində kod belə görünür:
var a;
console.log(a); // undefined
a = 5;
console.log(a); // 5
2. let və const ilə hoisting
let və const ilə elan edilən dəyişənlər də hoisting olunur, amma onlar Temporal Dead Zone (TDZ) adlanan bir mərhələyə düşürlər.
Bu zaman dəyişən elan olunana qədər istifadə edilərsə, ReferenceError səhvi alınır.
Nümunə:
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 10;
Nümunə const üçün də eynidir.
Bu kod belə görünür:
// b hoisted olur, amma dəyəri yoxdur, TDZ-dadır
let b;
console.log(b); // ReferenceError
b = 10;
Funksiyalarda hoisting
Funksiya elan etmənin (function declaration) və funksiya ifadələrinin (function expression) hoisting davranışı da fərqlidir.
1. Funksiya elanları (Function Declarations)
Funksiya elanları tam şəkildə hoisting olunur. Yəni funksiya koddan əvvəl çağırıla bilər.
Nümunə:
greet(); // "Salam!"
function greet() {
console.log('Salam!');
}
İzah:
JavaScript bütün funksiyanı yuxarı qaldırır, ona görə greet funksiyasını əvvəl çağırmaq mümkündür.
2. Funksiya ifadələri (Function Expressions)
Funksiya ifadələri hoisting olunur, amma onlar dəyişən kimi davranır. Əgər var ilə elan ediliblərsə, undefined, let və const ilə elan ediliblərsə, ReferenceError verirlər.
Qaldırılır, amma dəyişənin hoisting qaydalarına uyğundur
Praktik töhvsiyələr
let və const istifadə edin: Daha təhlükəsiz və oxunaqlı kod üçün.
Funksiyaları əvvəlcədən elan edin: Oxumağı və anlamağı asanlaşdırır.
Dəyişənləri istifadə etməzdən əvvəl elan edin: Səhv ehtimalını azaldır.
Nəticə
Hoisting JavaScript-in əsas və vacib xüsusiyyətlərindən biridir. Dəyişənlərin və funksiyaların necə “qaldırıldığını” bilmək kod səhvlərindən yayınmağa və proqramın necə işlədiyini daha yaxşı anlamağa kömək edir.
Unutmayın: var təhlükəli hoisting davranışına səbəb ola bilər, let və const isə daha təhlükəsizdir!
Java proqramlaşdırma dilində operatorlar dəyişənlər və dəyərlər üzərində müxtəlif əməliyyatlar aparmaq üçün istifadə olunur. Operatorlar proqramda əsas riyazi, məntiqi və müqayisə əməliyyatlarını icra etməyə imkan verir. Bu məqalədə Java-da istifadə olunan əsas operator növlərinə baxacağıq.
1. Riyazi operatorlar (Arithmetic operators)
Riyazi operatorlar ədədi dəyərlər üzərində toplama, çıxma, vurma və bölmə kimi əməliyyatlar aparır.
Əsas riyazi operatorlar:
+ (toplama)
- (çıxma)
* (vurma)
/ (bölmə)
% (modul - qalıq tapmaq)
Nümunə:
int a = 10;
int b = 3;
System.out.println(a + b); // 13
System.out.println(a % b); // 1
2. Təkli operatorlar (Unary operators)
Təkli operatorlar bir operand üzərində işləyir. Onlar dəyişənin dəyərini artırmaq, azaltmaq və ya mənfi etmək üçün istifadə olunur.
Əsas təkli operatorlar:
+ (müsbət etmək)
- (mənfi etmək)
++ (1 artırmaq)
-- (1 azaltmaq)
! (məntiqi inkar)
Nümunə:
int x = 5;
System.out.println(++x); // 6
System.out.println(--x); // 5
System.out.println(-x); // -5
3. Müqayisə operatorları (Relational operators)
Müqayisə operatorları iki dəyəri müqayisə edir və nəticə olaraq true və ya false verir.
Əsas müqayisə operatorları:
== (bərabərdir)
!= (bərabər deyil)
> (böyükdür)
< (kiçikdir)
>= (böyük və ya bərabərdir)
<= (kiçik və ya bərabərdir)
Nümunə:
int a = 5, b = 10;
System.out.println(a > b); // false
System.out.println(a <= b); // true
4. Məntiqi operatorlar (Logical operators)
Məntiqi operatorlar bir neçə şərtin birgə qiymətləndirilməsində istifadə olunur.
Bitiş operatorları ikili (binary) səviyyədə bitlər üzərində əməliyyat aparır.
Əsas bitiş operatorları:
& (AND)
| (OR)
^ (XOR)
~ (NOT)
<< (sola sürüşdürmə)
>> (sağa sürüşdürmə)
Nümunə:
int a = 5; // 0101
int b = 3; // 0011
System.out.println(a & b); // 1 (0001)
System.out.println(a | b); // 7 (0111)
6. Sürüşdürmə operatorları (Shift operators)
Sürüşdürmə operatorları bitləri müəyyən sayda sola və ya sağa hərəkət etdirir:
<< (sola sürüşdürmə)
>> (sağa sürüşdürmə)
Nümunə:
int a = 8; // 1000
System.out.println(a << 2); // 32 (100000)
System.out.println(a >> 2); // 2 (10)
7. Təyinat operatorları (Assignment operators)
Təyinat operatorları dəyişənə dəyər mənimsətmək üçün istifadə olunur.
Əsas təyinat operatorları:
= (sadə mənimsətmə)
+= (toplayaraq mənimsətmə)
-= (çıxaraq mənimsətmə)
*= (vurararaq mənimsətmə)
/= (bölərək mənimsətmə)
%= (modul alaraq mənimsətmə)
Nümunə:
int a = 5;
a += 3; // a = a + 3 -> a = 8
System.out.println(a);
8. Ternar operatoru (Ternary operator)
Ternar operator sadə if-else şərtinin qısa yazılış formasıdır:
şərt ? dəyər1 : dəyər2
Əgər şərt doğru olarsa, dəyər1, əks halda dəyər2 seçilir.
Nümunə:
int a = 5, b = 10;
int max = (a > b) ? a : b;
System.out.println(max); // 10
Nəticə
Java-da operatorlar proqramların əsas hissəsini təşkil edir və müxtəlif əməliyyatları yerinə yetirmək üçün istifadə olunur. Riyazi, məntiqi, təyinat və digər operator növlərini yaxşı başa düşmək, effektiv və düzgün kod yazmaq üçün vacibdir.
JavaScript-də iterators və generators, məlumatların ardıcıl emalını daha çevik və oxunaqlı şəkildə idarə etməyə imkan verir. Bu anlayışlar ES6 (ECMAScript 2015) ilə JavaScript dilinə əlavə edilib və xüsusilə custom iterable strukturların yaradılmasında, lazy evaluation və asinxron proqramlaşdırma sahələrində böyük əhəmiyyət daşıyır.
1. Iteration Protocols (İterasiya Protokolları)
JavaScript-də iterasiya aşağıdakı iki əsas protokol üzərindən qurulur:
1.1 Iterable protokolu
Bu protokol, obyektin Symbol.iterator adlı xüsusi bir metodu vasitəsilə iterasiya edilə biləcəyini göstərir. Bu metod geriyə bir iterator obyekt qaytarmalıdır.
1.2 Iterator protokolu
Iterator obyektində next() metodu mövcud olur. Bu metod hər çağırıldıqda obyektin növbəti dəyərini və iterasiyanın bitib-bitmədiyini bildirən { value, done } şəklində obyekt qaytarır.
String üzərində iterator istifadə
const text = "Rhyme";
const it = text[Symbol.iterator]();
console.log(it.next()); // { value: 'R', done: false }
Array elementlərini iterator ilə çap edilməsi
const cities = ["Sofia", "New Delhi", "Tokyo"];
const iterableCities = cities[Symbol.iterator]();
let result = iterableCities.next();
while (!result.done) {
console.log(result.value);
result = iterableCities.next();
}
Öz Iteratorumuzu yaratmaq
class Polygon {
constructor(...sides) {
this.sides = sides;
}
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.sides.length){
return { value: this.sides[index++], done: false }
}
return { done: true }
}
};
}
}
const poly = new Polygon(1, 2, 3, 4, 5);
for (let side of poly) {
console.log(side);
}
2 Generators ilə tanışlıq
Generatorlar function* sintaksisi ilə yaradılır. Onlar yield açar sözü ilə bir-bir dəyərlər qaytarır.
class Polygon {
constructor(...sides) {
this.sides = sides;
}
*[Symbol.iterator]() {
for (const side of this.sides) {
yield side;
}
}
}
ID Generatoru
function* idMaker() {
let id = 0;
while (true) {
yield id++;
}
}
const it = idMaker();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
Parametr ötürmək
function* observerGenerator() {
console.log('Generator created');
while (true) {
const value = yield;
console.log(`Value passed: ${value}`);
}
}
const it = observerGenerator();
it.next(); // Başlamaq üçün ilk çağırış
it.next(42); // Console: Value passed: 42
yield* ilə generatorlar içində generator
function* citiesGenerator() {
yield 'Paris';
yield* ['Rome', 'Florence', 'Berlin'];
}
const it = citiesGenerator();
for (const city of it) {
console.log(city);
}
Özünü yoxlmaq üçün mövzu üzrə tapşırıqlar
Misal 1: Sadə ədədləri verən generator funksiyası yazın
Bu məşqdə məqsəd, sadə ədədləri (prime numbers) ardıcıl olaraq verə bilən bir generator funksiyası yazmaqdır. Sadə ədədlər yalnız 1 və özündən başqa heç bir ədədə bölünməyən ədədlərdir (məsələn: 2, 3, 5, 7, 11, 13 və s.).
Yardımçı funksiya: isPrime(num)
const isPrime = num => {
for (let i = 2; i < num; i++) {
if (num % i === 0) {
return false;
}
}
return num > 1;
};
const isPrime = num => {
for (let i = 2; i < num; i++) {
if (num % i === 0) return false;
}
return num > 1;
}
function* primeNumbersGenerator() {
let num = 2;
while (true) {
if (isPrime(num)) yield num;
num++;
}
}
const primes = primeNumbersGenerator();
console.log(primes.next().value); // 2
console.log(primes.next().value); // 3
console.log(primes.next().value); // 5
Misal 2: Generator ilə sual-cavab prosesi
Bu məşqdə bir generator funksiyası yaradaraq, onun necə sual verə biləcəyini və sonradan həmin suala verilən cavabı next() metodu ilə qəbul edərək qiymətləndirəcəyini öyrənəcəyik. solveRiddle adlı generator bizə bir sual qaytarmalı (misal: What’s the Answer to the Ultimate Question of Life, the Universe, and Everything?) sonra daxil olan cavaba uyğun olaraq novbəti çağırışda true və ya false qaytarmalıdır. Misal üçün 42 yazılıbsa true.
function* solveRiddle() {
const question = "What's the Answer to the Ultimate Question of Life, the Universe, and Everything?";
const answer = yield question;
yield answer === 42;
}
const it = solveRiddle();
console.log(it.next().value); // sualı qaytarır
console.log(it.next(42).value); // true
Misal 3: Massivin Flatten olunması (Yastılanması)
Bu məşqdə məqsəd nested array-ləri (yəni iç-içə massivləri) istədiyimiz qədər dərinlikdə “yastılaşdırmaqdır” – yəni içərisindəki bütün elementləri tək səviyyəli array halına salmaqdır. flatten adlı bir generator function yazılmalıdır ki, o daxil olan array-in içərisindəki elementləri yield etməklə tək-tək qaytarsın. Əgər element array-dirsə və depth > 0-dırsa, həmin elementi yenidən flatten funksiyası ilə rekursiv şəkildə açmaq lazımdır (yield* istifadə etməklə).
JavaScript-də Iterators və Generators mövzusu, inkişaf etmiş iterasiya strukturları və performanslı həllər yaratmaq üçün güclü vasitələr təklif edir. Onlar həm custom data structures, həm də lazy evaluation, stream processing, və asinxron əməliyyatlar üçün vacibdir.
CSS Flexbox (Flexible Box Layout) veb səhifələrdə elementlərin elastik və səmərəli şəkildə yerləşdirilməsi üçün istifadə olunan güclü bir layout modelidir. Flexbox xüsusilə kompleks tərtibatlarda (naviqasiya panelləri, kart düzülüşləri, məzmun bölgüləri və s.) dizaynerlərə və proqramçılara böyük rahatlıq təmin edir. Bu məqalədə Flexbox-un əsas anlayışları, xüsusiyyətləri və istifadəsi ətraflı şəkildə izah olunacaq.
Flexbox-un əsas anlayışları
Flex Container və Flex Item
Flexbox istifadəsi üçün ilk öncə bir konteyner yaradılır. Bu konteyner display: flex; və ya display: inline-flex; ilə aktivləşdirilir:
.container {
display: flex;
}
Bu konteyner daxilində olan bütün child elementlər (flex item-lar) artıq flexbox qaydalarına uyğun şəkildə davranacaq.
Əsas istiqamət (Main Axis) və Çarpaz istiqamət (Cross Axis)
Flexbox-da iki əsas ox anlayışı var:
Main Axis – elementlərin düzüləcəyi əsas istiqamət.
Cross Axis – əsas oxun əksi istiqamət.
flex-direction xüsusiyyəti main axis-in istiqamətini təyin edir:
Elementlərin əsas oxa görə yerləşdirilməsi: justify-content
Flex item-ları əsas ox üzrə necə yerləşdirmək istədiyinizi təyin edir:
.container {
justify-content: flex-start; /* Sol tərəfə düzülür */
justify-content: flex-end; /* Sağ tərəfə düzülür */
justify-content: center; /* Ortaya düzülür */
justify-content: space-between;/* Aralarında boşluq, kənarlar sıx */
justify-content: space-around; /* Ətrafında bərabər boşluq */
justify-content: space-evenly; /* Bütün boşluqlar bərabər */
}
Elementlərin çarpaz oxa görə yerləşdirilməsi: align-items
Flexbox, CSS-də çox güclü və geniş istifadə olunan layout texnologiyasıdır. Əgər dizaynınız dinamik və cavab verən olmalıdırsa, Flexbox sizin üçün ideal seçimdir. Tətbiqatlarda daha çevik, daha sadə və daha təmiz kod üçün Flexbox istifadə etməyi öyrənmək böyük üstünlükdür.
Flexbox haqqında hazırlanmış bəlkə də ən yaxşı tutorial:
Abstraksiya (ing. Abstraction) obyekt yönümlü proqramlaşdırmanın (OOP) əsas prinsiplərindən biridir. Bu prinsip, istifadəçiyə yalnız vacib funksionallığı təqdim edərək, arxa plandakı mürəkkəb detalları gizlətməyə imkan verir. Beləliklə, istifadəçi obyektin nə etdiyini bilir, amma necə etdiyini bilməyə ehtiyac yoxdur.
Java-da abstraksiyanın əsasları
Java-da abstraksiya, əsasən iki vasitə ilə həyata keçirilir:
Abstrakt siniflər (Abstract Classes):abstract açar sözü ilə təyin olunan classlardı. Bu classlarda həm abstrakt (gövdəsiz) metodlar, həm də konkret (gövdəli) metodlar ola bilər. Əgər bir classda ən azı bir abstrakt metod varsa, həmin class mütləq abstract olaraq təyin olunmalıdır. Abstrakt classlardan birbaşa obyekt yaratmaq mümkün deyil; onları miras alan alt classlar abstrakt metodları təyin etməlidir.
İnterfeyslər (Interfaces): Yalnız abstrakt metodların (və ya Java 8-dən etibarən default və static metodların) təyin olunduğu strukturlardır. İnterfeyslər 100% abstraksiyanı təmin edir və bir class bir neçə interfeysi implement edə bilər, bu da çoxlu irsiliyi mümkün edir.
Abstraksiya nümunəsi
Aşağıdakı nümunədə Heyvan adlı abstrakt class və onu miras alan It classı göstərilmişdir:
abstract class Heyvan {
public abstract void sesCixar();
}
class It extends Heyvan {
public void sesCixar() {
System.out.println("It hürür");
}
}
public class Test {
public static void main(String args[]) {
Heyvan h = new It();
h.sesCixar(); // Çıxış: It hürür
}
}
Bu nümunədə, Heyvan classı abstrakt olaraq təyin olunub və sesCixar() metodu abstraktdır. It classı bu metodu öz tələblərinə uyğun təyin edir. Test classında isə Heyvan tipində bir istinad dəyişəni ilə It obyektinə müraciət olunur və sesCixar() metodu çağırıldıqda, It classındakı versiyası icra olunur.
Abstrakt classlar və İnterfeyslər arasındakı fərqlər
Xüsusiyyət
Abstrakt class
İnterfeys
Metodlar
Həm abstrakt, həm də konkret metodlar ola bilər
Yalnız abstrakt metodlar (Java 8-dən etibarən default və static metodlar da ola bilər)
İrsilik
Tək irsilik dəstəklənir
Çoxlu irsilik dəstəklənir
Obyekt yaratmaq
Birbaşa obyekt yaratmaq mümkün deyil
Birbaşa obyekt yaratmaq mümkün deyil
Nəticə
Java-da abstraksiya, proqramın mürəkkəbliyini azaltmağa və daha sadə, anlaşılan interfeyslər yaratmağa kömək edir. Bu prinsip, istifadəçiyə yalnız vacib funksionallığı təqdim edərək, arxa plandakı detalları gizlədir. Abstraksiya, proqramın saxlanmasını asanlaşdırır və genişləndirilməsini təmin edir.
Polimorfizm (yunanca “çox formalı”) obyekt yönümlü proqramlaşdırmanın (OOP) əsas prinsiplərindən biridir. Bu prinsip, obyektlərin bir neçə formada davranmasına imkan verir, yəni eyni metod adı ilə fərqli funksionallıqlar həyata keçirilə bilər.
Java-da polimorfizmin əsasları
Java-da polimorfizm, obyektin bir neçə formaya sahib olması deməkdir. Bu, əsasən iki şəkildə həyata keçirilir:
Kompilyasiya zamanı polimorfizm (Compile-time Polymorphism): Metodun aşırı yüklənməsi (method overloading) ilə əldə edilir. Eyni adlı metodlar fərqli parametr siyahıları ilə təyin olunur.
İcra zamanı polimorfizm (Runtime Polymorphism): Metodun yenidən təyin olunması (method overriding) ilə əldə edilir. Alt class, üst classdakı metodu öz tələblərinə uyğun yenidən təyin edir.
Java-da polimorfizmin istifadəsi
Polimorfizmin ən çox istifadə olunan forması, üst classın istinad dəyişəni ilə alt class obyektinə müraciət etməkdir. Bu, proqramın daha çevik və genişlənə bilən olmasını təmin edir.
Nümunə:
class Heyvan {
public void sesCixar() {
System.out.println("Heyvan səs çıxarır");
}
}
class It extends Heyvan {
public void sesCixar() {
System.out.println("It hürür");
}
}
public class Test {
public static void main(String args[]) {
Heyvan h = new It(); // Heyvan istinadı, It obyekti
h.sesCixar(); // Çıxış: It hürür
}
}
Bu nümunədə, Heyvan classın istinad dəyişəni ilə It classın obyektinə müraciət olunur. sesCixar() metodu çağırıldıqda, It classındakı versiyası icra olunur. Bu, icra zamanı polimorfizmin nümunəsidir.
Polimorfizmin üstünlükləri
Kodun yenidən istifadəsi: eyni metod adı ilə fərqli funksionallıqlar həyata keçirilə bilər.
Çeviklik: proqramın müxtəlif hissələrində eyni interfeys ilə fərqli davranışlar əldə etmək mümkündür.
Genişlənəbilənlik: yeni class əlavə edildikdə mövcud kodda dəyişiklik etmədən yeni funksionallıqlar əlavə etmək mümkündür.
Nəticə
Java-da polimorfizm, proqramın daha çevik, genişlənə bilən və təkrar istifadə oluna bilən olmasını təmin edir. Bu prinsip, obyektlərin müxtəlif formalarda davranmasına imkan verir və proqramın strukturunu daha səmərəli edir.
İrsilik (ing. Inheritance) obyekt yönümlü proqramlaşdırmanın (OOP) əsas prinsiplərindən biridir. Bu prinsip, bir classın başqa bir classın xüsusiyyətlərini (metodlar və dəyişənlər) əldə etməsinə imkan verir. İrsilik, kodun təkrar istifadəsini təmin edir və proqramın strukturunu daha sadə və anlaşılan edir.
Java-da irsiliyin əhəmiyyəti
Kodun yenidən istifadəsi: Bir dəfə yazılmış kodu digər classlarda təkrar istifadə etməyə imkan verir.
Genişlənəbilənlik: Mövcud classların funksionallığını genişləndirməyə şərait yaradır.
Metodun yenidən təyin edilməsi (Overriding): Alt classlarda üst classların metodlarını yenidən təyin etməyə imkan verir.
Abstraksiyanın əldə edilməsi: Abstraksiya prinsipi irsilik vasitəsilə həyata keçirilə bilər.
Java-da irsiliyin tətbiqi
Java-da irsiliyi tətbiq etmək üçün extends açar sözündən istifadə olunur. Bu, bir classın digər classın xüsusiyyətlərini miras almasını təmin edir.
İrsiliyin sintaksisi
class SuperClass {
// Üst classın xüsusiyyətləri
}
class SubClass extends SuperClass {
// Alt classın əlavə xüsusiyyətləri
}
Java-da irsilik nümunəsi
Aşağıdakı nümunədə Calculation adlı üst class və My_Calculation adlı alt class yaradılmışdır. My_Calculation classı Calculation classdan addition() və subtraction() metodlarını miras alır və əlavə olaraq multiplication() metodunu təqdim edir.
class Calculation {
int z;
public void addition(int x, int y) {
z = x + y;
System.out.println("Verilən ədədlərin cəmi: " + z);
}
public void subtraction(int x, int y) {
z = x - y;
System.out.println("Verilən ədədlərin fərqi: " + z);
}
}
public class My_Calculation extends Calculation {
public void multiplication(int x, int y) {
z = x * y;
System.out.println("Verilən ədədlərin hasilə: " + z);
}
public static void main(String args[]) {
int a = 20, b = 10;
My_Calculation demo = new My_Calculation();
demo.addition(a, b);
demo.subtraction(a, b);
demo.multiplication(a, b);
}
}
Java-da irsiliyin növləri
Java-da əsasən üç növ irsilik mövcuddur:
Tək irsilik (Single Inheritance): Bir alt class bir üst classdan miras alır.
Çoxsəviyyəli irsilik (Multilevel Inheritance): Bir class başqa bir classdan miras alır və həmin classda başqa bir classdan miras alır.
Hierarxik irsilik (Hierarchical Inheritance): Bir neçə alt class eyni üst classdan miras alır.
Qeyd: Java çoxlu irsiliyi (Multiple Inheritance) classlar səviyyəsində dəstəkləmir. Bu, “diamond problem” adlanan qeyri-müəyyənlikdən qaçmaq üçün nəzərdə tutulmuşdur. Lakin, Java interfeyslər vasitəsilə çoxlu irsiliyi təmin edir.
Nəticə
Java-da irsilik, proqramın strukturunu daha səmərəli və təkrar istifadə edilə bilən hala gətirir. Bu prinsip, kodun saxlanmasını asanlaşdırır və proqramın genişləndirilməsini təmin edir. İrsiliyin düzgün tətbiqi, obyekt yönümlü proqramlaşdırmanın gücündən tam istifadə etməyə imkan verir.
Enkapsulyasiya obyekt yönümlü proqramlaşdırmanın (OOP) əsas prinsiplərindən biridir. Bu prinsip, məlumatların (dəyərlərin) və həmin məlumatlar üzərində işləyən metodların bir yerdə saxlanılmasını təmin edir. Java-da enkapsulyasiya, class daxilindəki dəyişənlərin digər classlardan gizlədilməsi və yalnız həmin classın metodları vasitəsilə əldə olunması ilə həyata keçirilir. Bu səbəbdən, enkapsulyasiya tez-tez “məlumatların gizlədilməsi” (data hiding) adlandırılır.
Java-da enkapsulyasiya necə tətbiq olunur?
Java-da enkapsulyasiyanı həyata keçirmək üçün aşağıdakı addımlar izlənilir:
Dəyişənləri private elan etmək: Bu, həmin dəyişənlərin yalnız aid olduqları class daxilində əlçatan olmasını təmin edir.
publicgetter və setter metodları təmin etmək: Bu metodlar vasitəsilə dəyişənlərin dəyərləri oxunur və dəyişdirilir.
Bu yanaşma, classın daxilindəki məlumatların birbaşa müdaxilədən qorunmasını və yalnız müəyyən edilmiş metodlar vasitəsilə idarə olunmasını təmin edir.
Java-da enkapsulyasiya nümunəsi
Aşağıdakı nümunə, Java-da enkapsulyasiyanın necə tətbiq olunduğunu göstərir:
public class EncapTest {
private String name;
private String idNum;
private int age;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getIdNum() {
return idNum;
}
public void setAge(int newAge) {
age = newAge;
}
public void setName(String newName) {
name = newName;
}
public void setIdNum(String newId) {
idNum = newId;
}
}
Bu classdaname, idNum və age dəyişənləri private olaraq elan edilmişdir, yəni onlar yalnız EncapTest classı daxilində əlçatandır. Bu dəyişənlərin dəyərlərini oxumaq və dəyişdirmək üçün isə publicgetter və setter metodları təmin olunmuşdur.
Enkapsulyasiyanın üstünlükləri
Məlumatların qorunması: dəyişənlər birbaşa müdaxilədən qorunur, bu da proqramın təhlükəsizliyini artırır.
Modul dizayn: classlar arasında əlaqələr minimuma endirilir, bu da proqramın saxlanmasını və genişləndirilməsini asanlaşdırır.
Kodun oxunaqlılığı və idarə olunması: dəyişənlərə yalnız müəyyən metodlar vasitəsilə daxil olmaq, kodun daha oxunaqlı və idarəolunan olmasını təmin edir.
Nəticə
Java-da enkapsulyasiya, proqramın təhlükəsizliyini və strukturlaşdırılmasını təmin edən vacib bir OOP prinsipidir. Dəyişənlərin private elan edilməsi və publicgetter və setter metodları vasitəsilə idarə olunması, məlumatların qorunmasını və proqramın daha etibarlı olmasını təmin edir. Bu yanaşma, böyük və kompleks proqramların inkişafında xüsusilə faydalıdır.
Radix Sort-un Java proqramlaşdırma dilində implementasiyası
import java.util.ArrayList;
import java.util.List;
public class RadixSort {
// Ədədin müəyyən mövqedəki rəqəmini qaytarır
public static int getDigit(int num, int place) {
return (int)(Math.abs(num) / Math.pow(10, place)) % 10;
}
// Ədədin neçə rəqəmdən ibarət olduğunu tapır
public static int digitCount(int num) {
if (num == 0) return 1;
return (int)Math.floor(Math.log10(Math.abs(num))) + 1;
}
// Massivdə ən çox rəqəmə sahib ədədin rəqəm sayını tapır
public static int mostDigits(int[] nums) {
int maxDigits = 0;
for (int num : nums) {
maxDigits = Math.max(maxDigits, digitCount(num));
}
return maxDigits;
}
// Radix Sort-un əsas funksiyası
public static int[] radixSort(int[] nums) {
int maxDigitCount = mostDigits(nums);
for (int k = 0; k < maxDigitCount; k++) {
List<List<Integer>> digitBuckets = new ArrayList<>();
// 0-dan 9-a qədər 10 bucket (siyahı) yaradılır
for (int i = 0; i < 10; i++) {
digitBuckets.add(new ArrayList<>());
}
// Ədədlər müvafiq bucket-lərə yerləşdirilir
for (int num : nums) {
int digit = getDigit(num, k);
digitBuckets.get(digit).add(num);
}
// Bütün bucket-lər birləşdirilərək yeni massiv yaradılır
int index = 0;
for (List<Integer> bucket : digitBuckets) {
for (int num : bucket) {
nums[index++] = num;
}
}
}
return nums;
}
// Test məqsədli əsas metod
public static void main(String[] args) {
int[] data = {170, 45, 75, 90, 802, 24, 2, 66};
int[] sorted = radixSort(data);
for (int num : sorted) {
System.out.print(num + " ");
}
}
}
Radix Sort — müqayisəyə əsaslanmayan (non-comparative) sıralama alqoritmidir. Bu alqoritm elementləri rəqəmlərinə (yəni rəqəm mövqelərinə) görə bucket adlı qruplara bölərək işləyir. Əməliyyat ya ən az əhəmiyyətli rəqəmdən (LSD - least significant digit) başlayaraq, ya da ən çox əhəmiyyətli rəqəmdən (MSD - most significant digit) başlayaraq aparılır. Bu proses, bütün rəqəmlər nəzərə alınana qədər davam edir.
Radix Sort, xüsusilə tam ədədlərin sıralanmasında effektivdir və O(nk) (k = ən böyük rəqəm sayı) kimi xətti zaman mürəkkəbliyinə malikdir. Bu, onu böyük verilənlər üçün ənənəvi müqayisəyə əsaslanan sıralama alqoritmlərindən daha sürətli edir. Lakin bu alqoritm üçün bütün ədədlərin eyni sayda rəqəmə sahib olması və ya bərabərləşdirilməsi vacibdir.
İmplementasiya
getDigit - Verilmiş ədədin istənilən rəqəmini almaq
Bu funksiya num ədədinin i-ci mövqedəki rəqəmini qaytarır:
mostDigits - Massivdə ən çox rəqəmə sahib ədədin rəqəm sayını tapmaq
Bu funksiya bir massivdəki ən böyük rəqəm sayını tapır:
const mostDigits = (numberArray) => {
let max = 0;
for (let i = 0; i < numberArray.length; i++) {
max = Math.max(max, digitCount(numberArray[i]));
}
return max;
}
Radix Sort-un özünün tətbiqi
Bir massiv qəbul edən radixSort funksiyasını yaradın;
Ən böyük ədədin neçə rəqəmə sahib olduğunu mostDigits funksiyası ilə tapın;
k = 0-dan başlayaraq bu ən böyük rəqəm sayına qədər dövr yaradın;
Hər iterasiyada:
digitBuckets adlı 0-dan 9-a qədər 10 boş qutu yaradın;
Ədədləri k-cı rəqəmlərinə görə uyğun qutulara yerləşdirin;
Massivi bucket-lərdəki dəyərlərlə yenidən qurun ([].concat(...digitBuckets));
Sonda sıralanmış massivi qaytarın;
Tam Həll:
const radixSort = (array) => {
const largest = mostDigits(array);
for (let k = 0; k < largest; k++) {
const digitBuckets = Array.from({ length: 10 }, () => []);
for (let i = 0; i < array.length; i++) {
const digit = getDigit(array[i], k);
digitBuckets[digit].push(array[i]);
}
array = [].concat(...digitBuckets);
}
return array;
}
Nəticə
Radix Sort — sadə, amma güclü bir alqoritmdir. Xüsusilə tam ədədlərlə işləyən və böyük verilənlərdə performans tələb edən sistemlərdə çox istifadə olunur. Əgər ədədlər eyni uzunluqda deyilsə, 0 ilə doldurularaq eyni uzunluğa gətirilə bilər. Radix Sort-un müqayisəyə əsaslanan sort alqoritmlərindən fərqli olaraq rəqəm əsaslı olması onu unikal və fərqli edir.