Δημοσιεύτηκε: 20 Ιαν 2014, 21:03
από tr3quart1sta
Ανοίγω αυτό το thread, για να γνωρίσουμε και να συζητήσουμε τα περί του συναρτησιακού προγραμματισμού.

Οι περισσότεροι από εσάς που έχετε ασχοληθεί με τον προγραμματισμό, συνήθως δίνετε μία ακολουθία από εντολές οι οποίες θα πρέπει να εκτελεστούνε με μία συγκεκριμένη σειρά. Χρησιμοποιείτε μεταβλητές οι οποίες αποθηκεύουν προσωρινά μια τιμή που πιθανότατα θα αλλάξει κάποια στιγμή. Δημιουργείτε βρόγχους, ώστε να επαναλάβετε ορισμένες εντολές πολλές φορές. Ίσως κατασκευάζετε αντικείμενα, για να δώσετε νέο νόημα στα δεδομένα σας, ακολουθώντας το προγραμματιστικό παράδειγμα του αντικειμενοστρεφούς προγραμματισμού.

Υπάρχουν, όμως, κι άλλες μεθόδοι με τις οποίες μπορεί κανείς να προσεγγίσει ένα υπολογιστικό πρόβλημα. Στον συναρτησιακό προγραμματισμό, λοιπόν, αντί για αντικείμενα στο επίκεντρο βρίσκονται οι μαθηματικές συναρτήσεις. Αυτές οι συναρτήσεις εστιάζουν κυρίως στο "τι" θέλουμε να πετύχουμε και όχι στο "πως" θέλουμε να το πετύχουμε.

Μία μεγάλη διαφορά που μπορούμε να βρούμε στον συναρτησιακό προγραμματισμό, είναι η απουσία μεταβλητών! Δεν υπάρχει, δηλαδή, μία "κατάσταση" στην οποία βρίσκεται το πρόγραμμα που μπορεί να αλλάξει αργότερα. Υπάρχουν μόνο οι συναρτήσεις, που με την είσοδο των ίδιων παραμέτρων θα πρέπει πάντα να επιστρέφουνε το ίδιο αποτέλεσμα, όσες φορές και να εκτελεστούνε και δεν τροποποιούν τίποτα (οι λεγόμενες "pure" συναρτήσεις). Μπορούμε να δηλώσουμε και σταθερές, όπου στην ουσία έχουμε μια συνάρτηση που δεν δέχεται παραμέτρους και επιστρέφει πάντα την ίδια συγκεκριμένη τιμή.

Με την απουσία μεταβλητών παρουσιάζεται ένα μικρό "πρόβλημα" στις δομές επανάληψης, καθώς συνήθως απαιτείται κάποιος μετρητής που θα ορίσει το τέλος της επανάληψης μετά από έναν συγκεκριμένο αριθμό επαναλήψεων. Την λύση έρχεται να μας την προσφέρει η μέθοδος της αναδρομής, όπου τις περισσότερες φορές η συνάρτηση καλεί τον εαυτό της, έως ότου εκπληρωθεί η συνθήκη διακοπής της μεθόδου.

Είναι σύνηθες φαινόμενο στον συναρτησιακό προγραμματισμό, οι συναρτήσεις να δέχονται ως παραμέτρους όχι μόνο συγκεκριμένες τιμές, αλλά και συναρτήσεις! Τέτοιου είδους συναρτήσεις ονομάζονται high order functions. Σε άλλες περιπτώσεις, μπορεί μια συνάρτηση να επιστρέψει μια άλλη συνάρτηση, όπου βλέπουμε το φαινόμενο του partial application.

Τι προτερήματα μπορούμε να λάβουμε από αυτό το είδος προγραμματισμού?

α) Αφού δεν μπορούμε να αλλάξουμε κάποια κατάσταση, σημαίνει ότι το προγραμμά μας δεν έχει "παρενέργειες" (side-effects). Αυτό μας εγγυά λιγότερα σφάλματα στον κώδικα.

β) Ως αποτέλεσμα, η αποσφαλμάτωση του προγράμματος γίνεται πιο εύκολη. Επίσης μπορεί να γίνει και απόδειξη ορθότητας, καθώς μιλάμε για μαθηματικές συναρτήσεις.

γ) Η διαδικασία της παράλληλης εκτέλεσης γίνεται πολύ πιο εύκολη, επειδή τα προβλήματα βρίσκονται πολλές φορές στον συγχρονισμό της πρόσβασης σε μεταβλητές των οποίων η τιμή μπορεί να αλλάξει. Και δεν έχει σημασία η σειρά με την οποία θα εκτελεστούνε οι συναρτήσεις.

δ) Μπορούμε να έχουμε πιο σύντομο και "καθαρό" κώδικα (που επίσης βοηθάει σε πιο ορθό κώδικα). Δείτε ένα παράδειγμα quicksort γραμμένο σε Java και Haskell (pure γλώσσα συναρτησιακού προγραμματισμού) αντίστοιχα:

Sorting a list of numbers: [3,1,4,1,5,9] -> [1,1,3,4,5,9]

Java:

Μορφοποιημένος Κώδικας: Επιλογή όλων
private int[] numbers;

void sort(int[] values) {

if (values == null || values.length == 0) return;

numbers = values;

quicksort(0, values.length -1);

}



void quicksort(int low, int high) {

int i = low, j = high;

int pivot = nubers[low];

while (i <= j) {

while (numbers[i] < pivot) i++;

while (numbers[j] > pivot) j--;

if (i <= j) { exchange(i,j); i++; j--; }

}

if (low < j) quicksort(low, j);

if (i < high) quicksort(i, high);

}



void exchange(int i, int j) {

int temp = numbers[i];

numbers[i] = numbers[j];

numbers[j] = temp;

}

Haskell:

Μορφοποιημένος Κώδικας: Επιλογή όλων
sort[] = []

sort(x:xs) = sort ys ++ [x] ++ sort zs

where ys = [y | y <- xs, y <= x]

zs = [z | z <- xs, x < z]


ε) Γίνεται πιο εύκολη η διάσπαση του προγράμματος σε modules, όπου έχουμε ομάδες συναρτήσεων που ανήκουνε στην ίδια κατηγορία.

Ακόμα και αν κάποιος δεν ασχοληθεί με μία καθαρά συναρτησιακή γλώσσα προγραμματισμού, βλέπουμε άλλες γνωστές γλώσσες να υιοθετούν στοιχεία και χαρακτηριστικά από αυτό το είδος. Για παράδειγμα βλέπουμε η Java στην έκδοση 8 να εισάγει τα λεγόμενα lambda expressions. Στην ουσία πρόκειται για ανώνυμες συναρτήσεις που πολλές φορές γράφονται απευθείας μέσα στην παράμετρο κάποιας άλλης συνάρτησης. Ακόμα και η κυρίαρχη γλώσσα στο Web, η Javascript προσφέρει παρόμοια λειτουργικότητα. Ένα παράδειγμα σε κώδικα:

Μορφοποιημένος Κώδικας: Επιλογή όλων
function say(word) {

console.log(word);

}



function execute(someFunction, value) {

someFunction(value);

}



execute(say, "Hello");


Μπορούμε να το γράψουμε και με άλλο τρόπο:

Μορφοποιημένος Κώδικας: Επιλογή όλων
function execute(someFunction, value) {

someFunction(value);

}



execute(function(word){ console.log(word) }, "Hello");


Με αυτόν τον τρόπο, προσφέρουμε μια επιπλέον ευελιξία στον κώδικα, καθώς η λειτουργικότητα μιας συνάρτησης μπορεί να αλλάξει δυναμικά ανάλογα με ποιά ή ποιές άλλες συναρτήσεις δέχεται ως είσοδο!

Πιστεύω ότι ο συναρτησιακός προγραμματισμός είναι μία από τις πιό ενδιαφέρουσες κατηγορίες προγραμματισμού και είναι must για κάθε developer να γνωρίζει παραδείγματα από διάφορες μορφές προγραμματισμού.

Για όποιον ενδιαφέρεται για παραπάνω πληροφορίες, μπορεί να τις βρεί σε μερικά από τα παρακάτω links:

Functional Programming is black magic.

Ένα πολύ καλό tutorial

Ένα βιβλίο

Ένα σύντομο διαδραστικό online tutorial

Εν συντωμία

Μάθημα στα ελληνικά!

Για τυχόν λάθη ή σημαντικά σημεία που ίσως να ξέχασα, πείτε μου να τα σημπληρώσω. Φυσικά περιμένω και τις δικές σας απόψεις πάνω στο functional programming!