Qual è la differenza tra linguaggi di programmazione funzionali e imperativi?


159

La maggior parte dei linguaggi tradizionali, compresi i linguaggi di programmazione orientata agli oggetti (OOP) come C #, Visual Basic, C ++ e Java sono stati progettati per supportare principalmente la programmazione imperativa (procedurale), mentre i linguaggi come Haskell / gofer sono puramente funzionali. Qualcuno può approfondire qual è la differenza tra questi due modi di programmare?

So che dipende dalle esigenze dell'utente scegliere il modo di programmare, ma perché si consiglia di apprendere linguaggi di programmazione funzionali?



1
controlla quest'altro [post] [1]. Descrive chiaramente le differenze. [1]: stackoverflow.com/questions/602444/…
theta,

Risposte:


160

Definizione: un linguaggio imperativo utilizza una sequenza di affermazioni per determinare come raggiungere un determinato obiettivo. Si dice che queste affermazioni cambino lo stato del programma mentre ognuno viene eseguito a sua volta.

Esempi: Java è un linguaggio imperativo. Ad esempio, è possibile creare un programma per aggiungere una serie di numeri:

 int total = 0;
 int number1 = 5;
 int number2 = 10;
 int number3 = 15;
 total = number1 + number2 + number3; 

Ogni istruzione cambia lo stato del programma, dall'assegnazione di valori a ciascuna variabile all'aggiunta finale di tali valori. Usando una sequenza di cinque affermazioni al programma viene esplicitamente spiegato come sommare i numeri 5, 10 e 15.

Linguaggi funzionali: il paradigma di programmazione funzionale è stato creato esplicitamente per supportare un approccio funzionale puro alla risoluzione dei problemi. La programmazione funzionale è una forma di programmazione dichiarativa.

Vantaggi delle funzioni pure: il motivo principale per implementare trasformazioni funzionali come funzioni pure è che le funzioni pure sono componibili: cioè autosufficienti e apolidi. Queste caratteristiche offrono numerosi vantaggi, tra cui: Maggiore leggibilità e manutenibilità. Questo perché ogni funzione è progettata per eseguire un'attività specifica alla luce dei suoi argomenti. La funzione non si basa su alcuno stato esterno.

Sviluppo reiterativo più semplice. Poiché il codice è più facile da refactoring, le modifiche alla progettazione sono spesso più facili da implementare. Ad esempio, supponiamo di scrivere una trasformazione complicata e quindi di rendersi conto che parte del codice viene ripetuto più volte nella trasformazione. Se rifletti con un metodo puro, puoi chiamare il tuo metodo puro a piacimento senza preoccuparti degli effetti collaterali.

Test e debug più semplici. Poiché le funzioni pure possono essere testate più facilmente in modo isolato, è possibile scrivere un codice di test che chiama la funzione pura con valori tipici, casi limite validi e casi limite non validi.

Per le lingue OOP People o Imperative:

I linguaggi orientati agli oggetti sono utili quando hai un set fisso di operazioni sulle cose e man mano che il tuo codice si evolve, aggiungi principalmente nuove cose. Ciò può essere ottenuto aggiungendo nuove classi che implementano metodi esistenti e le classi esistenti vengono lasciate sole.

I linguaggi funzionali sono buoni quando hai un set fisso di cose e man mano che il tuo codice si evolve, aggiungi principalmente nuove operazioni su cose esistenti. Ciò può essere ottenuto aggiungendo nuove funzioni che calcolano con tipi di dati esistenti e le funzioni esistenti vengono lasciate sole.

Contro:

Dipende dai requisiti dell'utente per scegliere il modo di programmare, quindi c'è danno solo quando gli utenti non scelgono il modo corretto.

Quando l'evoluzione va nella direzione sbagliata, hai problemi:

  • L'aggiunta di una nuova operazione a un programma orientato agli oggetti potrebbe richiedere la modifica di molte definizioni di classe per aggiungere un nuovo metodo
  • L'aggiunta di un nuovo tipo di cose a un programma funzionale potrebbe richiedere la modifica di molte definizioni di funzioni per aggiungere un nuovo caso.

10
Una funzione pura in questo caso è l'equivalente di una funzione matematica. Gli stessi ingressi verranno sempre associati alle stesse uscite. Mancano anche effetti collaterali (oltre alla restituzione di un valore o valori), il che significa che il compilatore può fare alcune ottimizzazioni interessanti e rende più semplice eseguire la funzione in parallelo poiché non c'è nulla da affrontare.
WorBlux,

Quindi, i modi giusti e le migliori pratiche per comporre applicazioni oop sostenibili e testabili tendono a progettare un codice imperativo con uno stato mentale declerativo?
Kemal Gültekin,

4
Non vedo una chiara differenza nel testo in cui sono evidenziate le caratteristiche di ciascuna programmazione. La maggior parte della descrizione per la programmazione procedurale può essere scambiata dal testo di programmazione imperativo e viceversa.
AxeEffect,

7
Questa risposta cerca di chiarire cos'è la programmazione funzionale ma non si preoccupa nemmeno di definire cosa sia una funzione pura. Non vedo come chiunque possa leggere questa risposta e venire via fiducioso nel conoscere la differenza tra programmazione dichiarativa e procedurale.
Ringo,

230

Ecco la differenza:

Imperativo:

  • Inizio
  • Accendi le scarpe taglia 9 1/2.
  • Fai spazio in tasca per conservare un array [7] di chiavi.
  • Metti le chiavi nella stanza per le chiavi in ​​tasca.
  • Entra nel garage.
  • Garage aperto.
  • Inserisci auto.

... e così via ...

  • Metti il ​​latte in frigorifero.
  • Fermare.

Dichiarativo, di cui funzionale è una sottocategoria:

  • Il latte è una bevanda salutare, a meno che tu non abbia problemi a digerire il lattosio.
  • Di solito, si conserva il latte in frigorifero.
  • Un frigorifero è una scatola che mantiene le cose fresche.
  • Un negozio è un luogo in cui gli articoli vengono venduti.
  • Per "vendita" intendiamo lo scambio di cose con denaro.
  • Inoltre, lo scambio di denaro per le cose si chiama "acquisto".

... e così via ...

  • Assicurati di avere latte in frigorifero (quando ne abbiamo bisogno - per linguaggi funzionali pigri).

Riepilogo: nei linguaggi imperativi dici al computer come cambiare bit, byte e parole nella sua memoria e in quale ordine. In quelli funzionali, diciamo al computer quali sono le cose, le azioni ecc. Ad esempio, diciamo che il fattoriale di 0 è 1 e il fattoriale di ogni altro numero naturale è il prodotto di quel numero e il fattoriale del suo predecessore. Non diciamo: per calcolare il fattoriale di n, riservare una regione di memoria e memorizzare 1 lì, quindi moltiplicare il numero in quella regione di memoria con i numeri da 2 a n e memorizzare il risultato nello stesso posto, e alla fine, la regione di memoria conterrà il fattoriale.


1
Grazie. Questo è un ottimo modo per vederlo.
L-Samuels,

5
Mi è piaciuta la tua spiegazione @Igno, ma qualcosa non mi è ancora chiaro. In dichiarativa, anche se solo dire stoffe, ma ancora è necessario modificare i bit e le modifiche apportate al stati nella macchina per procedere diritto. Mi confonde il fatto che in qualche modo Dichiarativo sia simile alla Programmazione procedurale (come le funzioni C) , eppure c'è ancora una grande differenza tra loro internamente. Le funzioni C non sono uguali alle funzioni nella programmazione funzionale (a livello di macchina)?
phoenisx,

11
@Igno, come Subroto, non capisco davvero la tua spiegazione. Sembra che ciò che hai scritto possa essere riassunto come: Hai bisogno di una risposta ... ottieni una risposta. sembra ignorare i bit importanti ed è così. Non capisco come puoi nascondere quella parte all'utente, a un certo punto qualcuno deve sapere come è stato fatto ... non puoi tenere il mago dietro la tenda per sempre.
Brett Thomas,

3
Questo non è in remoto ciò che intendo per programmazione funzionale. Pensavo che la programmazione funzionale fosse la rimozione di input e output nascosti dalle funzioni.
Ringo,

7
Spiegazione contorta.
JoeTidee,

14

La maggior parte dei linguaggi moderni sono in vario grado sia imperativi che funzionali ma per comprendere meglio la programmazione funzionale, sarà meglio prendere un esempio di linguaggio funzionale puro come Haskell in contrasto con il codice imperativo in un linguaggio non così funzionale come java / c #. Credo che sia sempre facile spiegare con l'esempio, quindi di seguito è uno.

Programmazione funzionale: calcolare fattoriale di n ie n! cioè nx (n-1) x (n-2) x ... x 2 X 1

-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution  

factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3

-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1  
-- | 3 x (2 x (1 x (1)) = 6

Si noti che Haskel consente il sovraccarico della funzione al livello del valore dell'argomento. Ora di seguito è riportato un esempio di codice imperativo in grado crescente di imperatività:

//somewhat functional way
function factorial(n) {
  if(n < 1) {
     return 1;
  }
  return n * factorial(n-1);   
}
factorial(3);

//somewhat more imperative way
function imperativeFactor(n) {
  int f = 1
  for(int i = 1; i <= n; i++) {
     f = f * i
  }
  return f;
}

Questa lettura può essere un buon riferimento per capire come il codice imperativo si concentri maggiormente su come parte, stato della macchina (i in for loop), ordine di esecuzione, controllo del flusso.

L'esempio successivo può essere visto approssimativamente come codice java / c # lang e la prima parte come limitazione del linguaggio stesso in contrasto con Haskell per sovraccaricare la funzione in base al valore (zero) e quindi si può dire che non è un linguaggio funzionale purista, dall'altro mano puoi dire che supporta il prog funzionale. in una certa misura.

Divulgazione: nessuno dei codici sopra è testato / eseguito ma si spera che sia abbastanza buono da trasmettere il concetto; apprezzerei anche i commenti per tale correzione :)


1
Non dovrebbe essere return n * factorial(n-1);?
jinawee,

@jinawee, grazie per averlo fatto notare, l'ho corretto dan * (n-1)
vecchio monaco il

10

La programmazione funzionale è una forma di programmazione dichiarativa, che descrive la logica del calcolo e l'ordine di esecuzione è completamente enfatizzato.

Problema: voglio cambiare questa creatura da cavallo a giraffa.

  • Allungare il collo
  • Gambe allungate
  • Applicare macchie
  • Dai alla creatura una lingua nera
  • Rimuovi la coda di cavallo

Ogni articolo può essere eseguito in qualsiasi ordine per produrre lo stesso risultato.

La programmazione imperativa è procedurale. Lo stato e l'ordine sono importanti.

Problema: voglio parcheggiare la macchina.

  1. Nota lo stato iniziale della porta del garage
  2. Fermare l'auto nel vialetto
  3. Se la porta del garage è chiusa, aprire la porta del garage, ricordare il nuovo stato; altrimenti continua
  4. Porta l'auto nel garage
  5. Chiudi la porta del garage

Ogni passo deve essere fatto per arrivare al risultato desiderato. Tirare nel garage mentre la porta del garage è chiusa comporterebbe una porta del garage rotta.


Vedo solo la differenza tra asincrono e sincronizzazione.
Vladimir Vukanac,

@VladimirVukanac async / sync è un meccanismo, non una forma di programmazione
Jakub Keller

2
Oh, grazie, ne cercherò di più. Saresti così gentile, per aggiornare il problema 1 in modo che sia uguale al problema 2 "Voglio parcheggiare la mia auto" ma scritto in modo funzionale di programmazione? Quindi il parallelismo sarà escluso.
Vladimir Vukanac,

6

La programmazione funzionale è "programmazione con funzioni", in cui una funzione ha alcune proprietà matematiche attese, inclusa la trasparenza referenziale. Da queste proprietà scaturiscono ulteriori proprietà, in particolare fasi di ragionamento familiari rese possibili dalla sostituibilità che portano a prove matematiche (cioè giustificando la fiducia in un risultato).

Ne consegue che un programma funzionale è semplicemente un'espressione.

Puoi facilmente vedere il contrasto tra i due stili notando i luoghi in un programma imperativo in cui un'espressione non è più referenzialmente trasparente (e quindi non è costruita con funzioni e valori e non può di per sé far parte di una funzione). I due luoghi più ovvi sono: mutazione (ad es. Variabili) altri flussi di controllo non locali di effetti collaterali (ad es. Eccezioni)

Su questo quadro di programmi come espressioni composti da funzioni e valori, è costruito un intero paradigma pratico di linguaggi, concetti, "schemi funzionali", combinatori e vari sistemi di tipo e algoritmi di valutazione.

Secondo la definizione più estrema, quasi tutti i linguaggi, anche C o Java, possono essere definiti funzionali, ma di solito le persone riservano il termine per linguaggi con astrazioni specificamente rilevanti (come chiusure, valori immutabili e ausili sintattici come la corrispondenza dei modelli). Per quanto riguarda l'uso della programmazione funzionale, implica l'uso di funzioni e crea codice senza effetti collaterali. usato per scrivere prove


3

Lo stile di programmazione imperativo è stato praticato nello sviluppo web dal 2005 fino al 2013.

Con la programmazione imperativa, abbiamo scritto passo per passo il codice che elencava esattamente cosa dovrebbe fare la nostra applicazione.

Lo stile di programmazione funzionale produce astrazione attraverso modi intelligenti di combinare le funzioni.

Si fa menzione della programmazione dichiarativa nelle risposte e riguardo a ciò dirò che la programmazione dichiarativa elenca alcune regole che dobbiamo seguire. Forniamo quindi ciò che chiamiamo stato iniziale alla nostra applicazione e lasciamo che quelle regole definiscano in che modo si comporta l'applicazione.

Ora, queste descrizioni rapide probabilmente non hanno molto senso, quindi passiamo attraverso le differenze tra programmazione imperativa e dichiarativa camminando attraverso un'analogia.

Immagina che non stiamo costruendo software, ma invece creiamo torte per vivere. Forse siamo dei cattivi fornai e non sappiamo come preparare una deliziosa torta come dovremmo.

Quindi il nostro capo ci fornisce un elenco di indicazioni, ciò che conosciamo come ricetta.

La ricetta ci dirà come fare una torta. Una ricetta è scritta in uno stile imperativo come questo:

  1. Mescola 1 tazza di farina
  2. Aggiungi 1 uovo
  3. Aggiungi 1 tazza di zucchero
  4. Versare il composto in una padella
  5. Metti la padella in forno per 30 minuti e 350 gradi F.

La ricetta dichiarativa farebbe quanto segue:

1 tazza di farina, 1 uovo, 1 tazza di zucchero - Stato iniziale

Regole

  1. Se tutto è mescolato, mettere in padella.
  2. Se tutto non miscelato, metterlo nella ciotola.
  3. Se tutto in padella, mettere in forno.

Quindi gli approcci imperativi sono caratterizzati da approcci graduali. Si inizia con il passaggio 1 e si passa al passaggio 2 e così via.

Alla fine si finisce con un prodotto finale. Quindi, facendo questa torta, prendiamo questi ingredienti, li mescoliamo, li mettiamo in una padella e nel forno e ottieni il tuo prodotto finale.

In un mondo dichiarativo, è diverso. Nella ricetta dichiarativa separeremmo la nostra ricetta in due parti separate, iniziamo con una parte che elenca lo stato iniziale della ricetta, come le variabili. Quindi le nostre variabili qui sono le quantità dei nostri ingredienti e il loro tipo.

Prendiamo lo stato iniziale o gli ingredienti iniziali e applichiamo loro alcune regole.

Quindi prendiamo lo stato iniziale e li passiamo continuamente attraverso queste regole fino a quando non otteniamo una torta di fragole al rabarbaro pronta da mangiare o qualsiasi altra cosa.

Quindi, in un approccio dichiarativo, dobbiamo sapere come strutturare correttamente queste regole.

Quindi le regole che potremmo voler esaminare i nostri ingredienti o dichiarare, se mescolate, metterle in una padella.

Con il nostro stato iniziale, ciò non corrisponde perché non abbiamo ancora miscelato i nostri ingredienti.

Quindi la regola 2 dice che se non si mescolano, mescolali in una ciotola. Va bene sì, si applica questa regola.

Ora abbiamo una ciotola di ingredienti misti come il nostro stato.

Ora applichiamo di nuovo quel nuovo stato alle nostre regole.

Quindi la regola 1 dice che se gli ingredienti vengono mescolati mettili in una padella, okay sì ora la regola 1 si applica, facciamolo.

Ora abbiamo questo nuovo stato in cui gli ingredienti vengono mescolati e in padella. La regola 1 non è più pertinente, la regola 2 non si applica.

La Regola 3 dice che se gli ingredienti sono in una padella, mettili nel forno, la regola è ciò che si applica a questo nuovo stato, lascialo fare.

E finiamo con una deliziosa torta di mele calda o altro.

Ora, se sei come me, potresti pensare, perché non stiamo ancora facendo una programmazione imperativa. Questo ha senso.

Bene, per flussi semplici sì, ma la maggior parte delle applicazioni web ha flussi più complessi che non possono essere correttamente catturati dal design della programmazione imperativa.

In un approccio dichiarativo, potremmo avere alcuni ingredienti iniziali o uno stato iniziale come textInput=“”una singola variabile.

Forse l'inserimento del testo inizia come una stringa vuota.

Prendiamo questo stato iniziale e lo applichiamo a una serie di regole definite nella tua applicazione.

  1. Se un utente inserisce del testo, aggiorna l'inserimento del testo. Bene, in questo momento non si applica.

  2. Se il modello è reso, calcola il widget.

  3. Se textInput viene aggiornato, eseguire nuovamente il rendering del modello.

Bene, nulla di tutto ciò si applica, quindi il programma attenderà semplicemente che si verifichi un evento.

Quindi ad un certo punto un utente aggiorna l'inserimento del testo e quindi potremmo applicare la regola numero 1.

Potremmo aggiornarlo a “abcd”

Quindi abbiamo appena aggiornato il nostro testo e gli aggiornamenti di input del testo, la regola numero 2 non si applica, la regola numero 3 dice se l'input di testo è aggiornato, che si è appena verificato, quindi riutilizza il modello e poi torniamo alla regola 2 che dice se il modello è reso , calcola il widget, ok andiamo a calcolare il widget.

In generale, come programmatori, vogliamo lottare per progetti di programmazione più dichiarativi.

L'imperativo sembra più chiaro ed evidente, ma un approccio dichiarativo si adatta molto bene per applicazioni più grandi.


2

• Lingue imperative:

  • Esecuzione efficiente

  • Semantica complessa

  • Sintassi complessa

  • La concorrenza è progettata dal programmatore

  • Test complessi, non hanno trasparenza referenziale, hanno effetti collaterali

  • Ha stato

• Lingue funzionali:

  • Semantica semplice

  • Sintassi semplice

  • Esecuzione meno efficiente

  • I programmi possono essere automaticamente resi simultanei

  • Test semplici, hanno trasparenza referenziale, non hanno effetti collaterali

  • Non ha stato

1

Penso che sia possibile esprimere la programmazione funzionale in modo imperativo:

  • Utilizzando un sacco di controllo dello stato di oggetti e if... else/ switchistruzioni
  • Alcuni meccanismi di timeout / attesa per prendersi cura dell'asincrono

Esistono enormi problemi con tale approccio:

  • Le regole / procedure sono ripetute
  • Statefulness lascia possibilità di effetti collaterali / errori

La programmazione funzionale, il trattamento di funzioni / metodi come oggetti e l'abbraccio dell'apolidia, è nata per risolvere quei problemi in cui credo.

Esempio di utilizzo: applicazioni front-end come Android, iOS o logiche di app web incl. comunicazione con il backend.

Altre sfide nella simulazione della programmazione funzionale con codice imperativo / procedurale:

  • Condizione di gara
  • Combinazione complessa e sequenza di eventi. Ad esempio, l'utente tenta di inviare denaro in un'app bancaria. Passaggio 1) Esegui tutte le seguenti operazioni in parallelo, procedi solo se tutto va bene a) Verifica se l'utente è ancora buono (frode, AML) b) controlla se l'utente ha abbastanza equilibrio c) Verifica se il destinatario è valido e buono (frode, AML) ecc. Passaggio 2) eseguire l'operazione di trasferimento Passaggio 3) Mostra aggiornamento sul saldo dell'utente e / o qualche tipo di tracciamento. Con RxJava, ad esempio, il codice è conciso e ragionevole. Senza di essa, posso immaginare che ci sarebbe un sacco di codice, disordinato e soggetto a errori

Credo anche che alla fine della giornata, il codice funzionale verrà tradotto in assembly o codice macchina che è imperativo / procedurale dai compilatori. Tuttavia, a meno che non si scriva assembly, poiché gli umani scrivono codice con un linguaggio di alto livello / leggibile dall'uomo, la programmazione funzionale è il modo di espressione più appropriato per gli scenari elencati


-1

So che questa domanda è più vecchia e altri l'hanno già spiegata bene, vorrei fare un esempio di problema che spiega lo stesso in termini semplici.

Problema: scrivere la tabella 1.

Soluzione: -

Dallo stile imperativo: =>

    1*1=1
    1*2=2
    1*3=3
    .
    .
    .
    1*n=n 

Per stile funzionale: =>

    1
    2
    3
    .
    .
    .
    n

Spiegazione in stile Imperativo scriviamo le istruzioni in modo più esplicito e che possono essere chiamate come in modo più semplificato.

Dove come nello stile funzionale, le cose che si spiegano da sole verranno ignorate.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.