Funksional proqramlama nədir?

Nasir Nasirli
Pragmatech
Published in
3 min readSep 7, 2020

Funksional proqramlama haqqında danışdıqda proqramçılar çox vaxt “immutable data”, “first class functions”, “tail call optimisation” kimi terminlərdən istifadə edirlər. Bunlar funksional proqramlamanın üstünlükləri olsa da bu terminlərinin adının çəkilməsi bu paradiqmanın başa düşülməsinə heç də kömək etmir.

Funksional proqramlamanı anlamaq üçün bir şeyi başa düşmək lazımdır: əlavə effektlərin olmaması. Funksiyalar xarici datadan asılı olmur, funksiyadan xaricdə olan dəyişənə müdaxilə etmir. Əslində, təcrübəli proqramçılar bilir ki, qlobal dəyişənlərə funksiya daxilindən istinad etmək təhlükəli əlavə effektlər yarada bilər.

Aşağıda funksional paradiqma məntiqində olmayan funksiyaya baxaq:

x = 0
def increment1():
global x
x += 1
return x

increment1 funksiyasında görürük ki, x dəyişəni qlobal elan olunub, funksiya heç bir arqument qəbul etmir və qlobal dəyişəni artırır.

def increment2(x):
return x + 1

increment2 funksiyasında isə funksiya müdaxilə etdiyi dəyişəni arqument kimi qəbul edib. Təbii ki, burada heç bir əlavə effektdən söhbət gedə bilməz, çünki x dəyişənin yaşama yeri ancaq funksiyanın daxilindədir.

Funksional proqramlama praktikasında kod yazmaq üçün bir neçə built-in funksiyalar vardır. Onlardan ən maraqlıları map və reduce funksiyalarıdır. Map funksiyanı və kolleksiya tipini(məsələn listi) arqument kimi alır və funksiyanı hər bir kolleksiya daxilindəki elementə tətbiq edir, yeni kolleksiya qaytarır.

def say_hello(name):
return 'Hello ' + name
names = ['Ehmed', 'Saleh', 'Gulzar']greetings = map(say_hello, names)print(list(greetings))
#['Hello Ehmed', 'Hello Saleh', 'Hello Gulzar']

Qeyd edim ki, map funksiyası map obyekti qaytarır, ona görə də greetings dəyişənini çap etmək üçün onu əvvəlcə listə çevirmək lazımdır.

Map funkiyası birinci arqument kimi lambda funksiya da qəbul edə bilər:

squares = map(lambda x: x ** 2, [1,2,3,4,5])
print(list(squares))
#[1,4,9,16,25]

Aşağıda başqa misala baxaq:

import random

names = ['Mary', 'Isla', 'Sam']
code_names = ['Mr. Pink', 'Mr. Orange', 'Mr. Blonde']

for i in range(len(names)):
names[i] = random.choice(code_names)

print(list(names))
#['Mr. Blonde', 'Mr. Blonde', 'Mr. Blonde']

Map ilə yazsaq,

import random

names = ['Mary', 'Isla', 'Sam']

secret_names = map(lambda x: random.choice(['Mr. Pink',
'Mr. Orange',
'Mr. Blonde']),
names)

kimi olacaq.

Reduce funksiyası list elementlərini kombinasiya edərək azaldır:

from functools import reducesum = reduce(lambda a, x: a + x, [0, 1, 2, 3, 4])

print(sum)

Burada x hazırda iterasiya olunan elementdir, a isə toplayıcıdır. a lambda funksiyasının əvvəlki element üzərində işləyərkən qaytardığı dəyərdir. reduce() elementlər üzəridə gəzir, hər dəfə lambdanı a və x üzərində run edir və nəticəni növbəti iterasiyanın a-sı kimi qaytarır.

Map və reduce istifadəsi bizə nə verir?

Əvvəla, bunların istifadəsi kodu xeyli qısaldır. İkincisi, iterasiya olunan kolleksiya və qaytarılmış nəticələr həmişə map və reduce-in eyni yerində olur. Üçüncüsü, for və ya while döngüsü döngüdən əvvəl elan olunmuş dəyişənin dəyərini dəyişə bilər. Map və reduce funksinal proqramlama elementləri sayılır. Dördüncüsü, bu funskiyaların istifadəsi proqramçının işini asanlaşdırır. Döngü istifadəsi zamanı biz kodu sətir-sətir analiz etməliyik, amma map və reduce zamanı biz bu prosesi abstraklaşdırırıq, mürəkkəb alqoritmləri asan həyata keçirə bilirik.

Funskiyalar paradiqmaya uyğun kod yazmaq üçün imperativ stildən çəkinmək lazımdır. Bunun üçün kodu uyğun funksiyaların içində yazmaq lazımdır.

from random import random

time = 5
car_positions = [1, 1, 1]

while time:
# decrease time
time -= 1

print('')
for i in range(len(car_positions)):
# move car
if random() > 0.3:
car_positions[i] += 1

# draw car
print('-' * car_positions[i])

Yuxarıdakı kod imperativ şəkildə yazılıb. Funksional versiya deklarativ olmalı idi, o necə yox, nə etmək lazım olduğunu göstərməli idi.

Funksional yazılışa baxaq:

from random import random

def move_cars():
for i, _ in enumerate(car_positions):
if random() > 0.3:
car_positions[i] += 1

def draw_car(car_position):
print('-' * car_position)

def run_step_of_race():
global time
time -= 1
move_cars()

def draw():
print('')
for car_position in car_positions:
draw_car(car_position)

time = 5
car_positions = [1, 1, 1]

while time:
run_step_of_race()
draw()

Kod işləməyə while döngüsündən başlayır. Oxucu döngüyə baxan kimi nə baş verdiyini dərhal anlayır. Əlavə şərhə ehtiyac yoxdur, funksional kod özü danışır.

İstifadə olunmuş mənbələr:

https://bit.ly/338RFki

--

--