Domanda

Ho letto gli articoli di Wikipedia per entrambi programmazione procedurale E programmazione funzionale, ma sono ancora un po' confuso.Qualcuno potrebbe ridurlo al nocciolo?

È stato utile?

Soluzione

Un linguaggio funzionale (idealmente) consente di scrivere una funzione matematica, ad es.una funzione che assume N argomenti e restituisce un valore.Se il programma viene eseguito, questa funzione viene valutata logicamente secondo necessità.1

Un linguaggio procedurale, invece, esegue una serie di sequenziale passi.(Esiste un modo per trasformare la logica sequenziale in logica funzionale chiamato stile di passaggio continuo.)

Di conseguenza, un programma puramente funzionale cede sempre lo stesso valore per un input e l'ordine di valutazione non è ben definito;il che significa che valori incerti come l'input dell'utente o valori casuali sono difficili da modellare in linguaggi puramente funzionali.


1 Come tutto il resto in questa risposta, questa è una generalizzazione.La proprietà di valutare un calcolo quando è necessario il suo risultato piuttosto che in sequenza dove viene chiamato è nota come “pigrizia”, e non tutti i linguaggi funzionali sono in realtà universalmente pigri, né la pigrizia è limitata alla programmazione funzionale.Piuttosto, la descrizione qui fornita fornisce una “struttura mentale” per pensare ai diversi stili di programmazione che non sono categorie distinte e opposte ma piuttosto idee fluide.

Altri suggerimenti

Fondamentalmente i due stili sono come Yin e Yang.Uno è organizzato, mentre l'altro è caotico.Ci sono situazioni in cui la programmazione funzionale è la scelta più ovvia e altre situazioni in cui la programmazione procedurale è la scelta migliore.Questo è il motivo per cui ci sono almeno due linguaggi che sono recentemente usciti con una nuova versione, che abbraccia entrambi gli stili di programmazione. ( Perl 6 E D 2 )

Procedurale:

  • L'output di una routine non ha sempre una correlazione diretta con l'input.
  • Tutto è fatto in un ordine specifico.
  • L'esecuzione di una routine può avere effetti collaterali.
  • Tende a enfatizzare l'implementazione delle soluzioni in modo lineare.

Perl6

D2

int factorial( int n ){

  int result = 1;

  for( ; n > 0 ; n-- ){
    result *= n;
  }

  return result;
}

Funzionale:

  • Spesso ricorsivo.
  • Restituisce sempre lo stesso output per un dato input.
  • L'ordine di valutazione è solitamente indefinito.
  • Deve essere apolide.cioè.Nessuna operazione può avere effetti collaterali.
  • Buon adattamento per l'esecuzione parallela
  • Tende a enfatizzare un approccio divide et impera.
  • Può avere la funzione di valutazione pigra.

Haskell

(copiato da Wikipedia );

fac :: Integer -> Integer

fac 0 = 1
fac n | n > 0 = n * fac (n-1)

o in una riga:

fac n = if n > 0 then n * fac (n-1) else 1

Perl6

D2

pure int factorial( invariant int n ){
  if( n <= 1 ){
    return 1;
  }else{
    return n * factorial( n-1 );
  }
}

Nota a margine:

Fattoriale è in realtà un esempio comune per mostrare quanto sia facile creare nuovi operatori in Perl 6 nello stesso modo in cui creeresti una subroutine.Questa funzionalità è così radicata in Perl 6 che la maggior parte degli operatori nell'implementazione Rakudo sono definiti in questo modo.Ti consente inoltre di aggiungere i tuoi multicandidati agli operatori esistenti.

Questo esempio mostra anche la creazione dell'intervallo (2..$n) e il metaoperatore di riduzione dell'elenco ([ OPERATOR ] LIST) combinato con l'operatore di moltiplicazione dell'infisso numerico.(*)
Mostra anche che puoi mettere --> UInt nella firma invece di returns UInt dopo ciò.

(Puoi farla franca iniziando la gamma con 2 poiché verrà restituito l'"operatore" moltiplicativo 1 quando chiamato senza argomenti)

Non ho mai visto questa definizione data altrove, ma penso che questo riassuma abbastanza bene le differenze fornite qui:

Funzionale la programmazione si concentra su espressioni

Procedurale la programmazione si concentra su dichiarazioni

Le espressioni hanno valori.Un programma funzionale è un'espressione il cui valore è una sequenza di istruzioni che il computer deve eseguire.

Le affermazioni non hanno valori e modificano invece lo stato di qualche macchina concettuale.

In un linguaggio puramente funzionale non ci sarebbero istruzioni, nel senso che non c'è modo di manipolare lo stato (potrebbero comunque avere un costrutto sintattico chiamato "istruzione", ma a meno che non manipoli lo stato non la chiamerei un'istruzione in questo senso ).In un linguaggio puramente procedurale non esisterebbero espressioni, tutto sarebbe un'istruzione che manipola lo stato della macchina.

Haskell sarebbe un esempio di linguaggio puramente funzionale perché non c'è modo di manipolare lo stato.Il codice macchina sarebbe un esempio di linguaggio puramente procedurale perché tutto in un programma è un'istruzione che manipola lo stato dei registri e della memoria della macchina.

La parte confusa è che la stragrande maggioranza dei linguaggi di programmazione contiene Entrambi espressioni e affermazioni, che consentono di mescolare paradigmi.Le lingue possono essere classificate come più funzionali o più procedurali in base a quanto incoraggiano l'uso di affermazioni o espressioni.

Ad esempio, C sarebbe più funzionale di COBOL perché una chiamata di funzione è un'espressione, mentre la chiamata di un sottoprogramma in COBOL è un'istruzione (che manipola lo stato delle variabili condivise e non restituisce un valore).Python sarebbe più funzionale di C perché consente di esprimere la logica condizionale come espressione utilizzando la valutazione di cortocircuito (test && path1 || path2 invece delle istruzioni if).Scheme sarebbe più funzionale di Python perché tutto in schema è un'espressione.

Puoi ancora scrivere in uno stile funzionale in un linguaggio che incoraggia il paradigma procedurale e viceversa.È semplicemente più difficile e/o più imbarazzante scrivere secondo un paradigma che non è incoraggiato dal linguaggio.

Nell'informatica, la programmazione funzionale è un paradigma di programmazione che tratta il calcolo come la valutazione di funzioni matematiche ed evita dati di stato e mutabili.Enfatizza l'applicazione delle funzioni, in contrasto con lo stile di programmazione procedurale che enfatizza i cambiamenti di stato.

Credo che la programmazione procedurale/funzionale/obiettiva riguardi il modo in cui affrontare un problema.

Il primo stile pianificherebbe tutto in passaggi e risolverebbe il problema implementando un passaggio (una procedura) alla volta.D'altra parte, la programmazione funzionale enfatizzerebbe l'approccio divide et impera, in cui il problema viene diviso in sottoproblemi, quindi ciascun sottoproblema viene risolto (creando una funzione per risolvere quel sottoproblema) e i risultati vengono combinati per creare la risposta all’intero problema.Infine, la programmazione oggettiva imiterebbe il mondo reale creando un mini-mondo all'interno del computer con molti oggetti, ognuno dei quali ha caratteristiche (in qualche modo) uniche e interagisce con gli altri.Da quelle interazioni emergerebbe il risultato.

Ogni stile di programmazione ha i suoi vantaggi e punti deboli.Quindi, fare qualcosa come "programmazione pura" (cioèpuramente procedurale - nessuno lo fa, tra l'altro, il che è un po' strano - o puramente funzionale o puramente oggettivo) è molto difficile, se non impossibile, tranne alcuni problemi elementari appositamente progettati per dimostrare il vantaggio di uno stile di programmazione (quindi, chiamiamo coloro che amano la purezza "weenie" :D).

Quindi, da questi stili, abbiamo linguaggi di programmazione progettati per essere ottimizzati per alcuni stili.Ad esempio, l'Assemblea è tutta una questione procedurale.Ok, la maggior parte dei primi linguaggi sono procedurali, non solo Asm, come C, Pascal (e Fortran, ho sentito).Quindi, abbiamo tutto il famoso Java nella scuola oggettiva (in realtà, anche Java e C# sono in una classe chiamata "orientata al denaro", ma questo è oggetto di un'altra discussione).Anche l'obiettivo è Smalltalk.Nella scuola funzionale, avremmo famiglie Lisp e ML "quasi funzionali" (alcuni le consideravano impure) e molti Haskell, Erlang, ecc.A proposito, ci sono molti linguaggi generali come Perl, Python, Ruby.

Per espandere il commento di Konrad:

Di conseguenza, un programma puramente funzionale restituisce sempre lo stesso valore per un input e l'ordine di valutazione non è ben definito;

Per questo motivo, il codice funzionale è generalmente più semplice da parallelizzare.Poiché (generalmente) non ci sono effetti collaterali delle funzioni e (generalmente) agiscono semplicemente in base ai loro argomenti, molti problemi di concorrenza scompaiono.

La programmazione funzionale viene utilizzata anche quando è necessario esserne capaci dimostrando il tuo codice è corretto.Questo è molto più difficile da fare con la programmazione procedurale (non facile con quella funzionale, ma comunque più semplice).

Disclaimer:Non utilizzo la programmazione funzionale da anni e solo di recente ho iniziato a esaminarla di nuovo, quindi potrei non essere completamente corretto qui.:)

Una cosa che non avevo visto veramente enfatizzata qui è che i linguaggi funzionali moderni come Haskell si basano più su funzioni di prima classe per il controllo del flusso che sulla ricorsione esplicita.Non è necessario definire ricorsivamente fattoriale in Haskell, come fatto sopra.Penso qualcosa del genere

fac n = foldr (*) 1 [1..n]

è una costruzione perfettamente idiomatica e molto più vicina nello spirito all'uso di un ciclo che all'uso della ricorsione esplicita.

Una programmazione funzionale è identica alla programmazione procedurale in cui sono presenti variabili globali non in uso.

I linguaggi procedurali tendono a tenere traccia dello stato (utilizzando variabili) e tendono ad essere eseguiti come una sequenza di passaggi.I linguaggi puramente funzionali non tengono traccia dello stato, utilizzano valori immutabili e tendono a essere eseguiti come una serie di dipendenze.In molti casi lo stato dello stack di chiamate conterrà le informazioni che sarebbero equivalenti a quelle che verrebbero archiviate nelle variabili di stato nel codice procedurale.

La ricorsione è un classico esempio di programmazione in stile funzionale.

Konrad ha detto:

Di conseguenza, un programma puramente funzionale restituisce sempre lo stesso valore per un input, e l'ordine di valutazione non è ben definito;il che significa che valori incerti come L'input dell'utente o i valori casuali sono difficili da modellare in linguaggi puramente funzionali.

L'ordine di valutazione in un programma puramente funzionale può essere difficile(più) su cui ragionare (soprattutto per pigrizia) o addirittura non importante, ma penso che dire che non è ben definito fa sembrare che non si possa dire se il programma sta funzionando lavorare affatto!

Forse una spiegazione migliore sarebbe che il flusso di controllo nei programmi funzionali si basa su quando è necessario il valore degli argomenti di una funzione.La cosa buona è che nei programmi ben scritti lo stato diventa esplicito:ogni funzione elenca i suoi input come parametri invece che arbitrariamente munging stato globale.Quindi, ad un certo livello, è più semplice ragionare sull'ordine di valutazione rispetto ad una funzione alla volta.Ogni funzione può ignorare il resto dell’universo e concentrarsi su ciò che deve fare.Se combinate, è garantito che le funzioni funzionino allo stesso modo[1] come se fossero isolate.

...I valori incerti come l'input dell'utente o i valori casuali sono difficili da modellare in linguaggi puramente funzionali.

La soluzione al problema dell'input nei programmi puramente funzionali è incorporare un linguaggio imperativo come a DSL utilizzando un'astrazione sufficientemente potente.Nei linguaggi imperativi (o non funzionali puri) questo non è necessario perché puoi "imbrogliare" e passare lo stato implicitamente e l'ordine di valutazione è esplicito (che ti piaccia o no).A causa di questo "imbroglio" e della valutazione forzata di tutti i parametri per ogni funzione, nei linguaggi imperativi 1) si perde la capacità di creare i propri meccanismi di flusso di controllo (senza macro), 2) il codice non è intrinsecamente thread-safe e/o parallelizzabile per impostazione predefinita, 3) e implementare qualcosa come l'annullamento (viaggio nel tempo) richiede un lavoro attento (il programmatore imperativo deve memorizzare una ricetta per ripristinare i vecchi valori!), Mentre la pura programmazione funzionale ti offre tutte queste cose, e qualcuna in più, potrei hanno dimenticato: "gratuitamente".

Spero che questo non sembri fanatismo, volevo solo aggiungere qualche prospettiva.La programmazione imperativa e soprattutto la programmazione a paradigmi misti in linguaggi potenti come C# 3.0 sono ancora modi totalmente efficaci per portare a termine le cose e non esiste una soluzione miracolosa.

[1] ...tranne eventualmente per quanto riguarda l'utilizzo della memoria (cfr.foldl e foldl' in Haskell).

Per espandere il commento di Konrad:

e l'ordine di valutazione non è ben definito

Alcuni linguaggi funzionali hanno quella che viene chiamata valutazione pigra.Ciò significa che una funzione non viene eseguita finché non è necessario il valore.Fino a quel momento ciò che viene trasmesso è la funzione stessa.

I linguaggi procedurali sono passo 1 passo 2 passo 3...se nel passaggio 2 dici aggiungi 2 + 2, lo fa subito.Nella valutazione pigra diresti "addiziona 2 + 2", ma se il risultato non viene mai utilizzato, l'addizione non verrà mai eseguita.

Se ne hai la possibilità, ti consiglio di procurarti una copia di Lisp/Scheme e di realizzare alcuni progetti al suo interno.La maggior parte delle idee che ultimamente sono diventate un carrozzone sono state espresse in Lisp decenni fa:programmazione funzionale, continuazioni (come chiusure), garbage collection, persino XML.

Quindi sarebbe un buon modo per ottenere un vantaggio su tutte queste idee attuali, e alcune altre ancora, come il calcolo simbolico.

Dovresti sapere a cosa serve la programmazione funzionale e a cosa non va bene.Non va bene per tutto.Alcuni problemi sono meglio espressi in termini di effetti collaterali, dove la stessa domanda dà risposte diverse a seconda di quando viene posta.

@Creighton:

In Haskell esiste una funzione di libreria chiamata Prodotto:

prouduct list = foldr 1 (*) list

o semplicemente:

product = foldr 1 (*)

quindi il fattoriale "idiomatico".

fac n = foldr 1 (*)  [1..n]

sarebbe semplicemente

fac n = product [1..n]

Programmazione Funzionale

num = 1 
def function_to_add_one(num):
    num += 1
    return num


function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)

#Final Output: 2

Programmazione procedurale

num = 1 
def procedure_to_add_one():
    global num
    num += 1
    return num


procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()

#Final Output: 6

function_to_add_one è una funzione

procedure_to_add_one è una procedura

Anche se esegui il file funzione cinque volte, ogni volta che tornerà 2

Se esegui il file procedura cinque volte, alla fine della quinta corsa ti darà 6.

La programmazione procedurale divide sequenze di istruzioni e costrutti condizionali in blocchi separati chiamati procedure parametrizzate su argomenti che sono valori (non funzionali).

La programmazione funzionale è la stessa, tranne per il fatto che le funzioni sono valori di prima classe, quindi possono essere passati come argomenti ad altre funzioni e restituiti come risultati dalle chiamate di funzioni.

Si noti che la programmazione funzionale è una generalizzazione della programmazione procedurale in questa interpretazione.Tuttavia, una minoranza interpreta "programmazione funzionale" come priva di effetti collaterali, il che è abbastanza diverso ma irrilevante per tutti i principali linguaggi funzionali tranne Haskell.

Per comprendere la differenza, è necessario comprendere che il paradigma "padrino" della programmazione sia procedurale che funzionale è il programmazione imperativa.

Fondamentalmente la programmazione procedurale è semplicemente un modo di strutturare programmi imperativi in cui il metodo principale di astrazione è la "procedura". (o "funzione" in alcuni linguaggi di programmazione).Anche la programmazione orientata agli oggetti è solo un altro modo di strutturare un programma imperativo, in cui lo stato è incapsulato in oggetti, diventando un oggetto con uno "stato corrente", inoltre questo oggetto ha un insieme di funzioni, metodi e altre cose che ti consentono di il programmatore manipola o aggiorna lo stato.

Ora, per quanto riguarda la programmazione funzionale, il sostanza nel suo approccio è quello di identificare quali valori assumere e come questi valori dovrebbero essere trasferiti.(quindi non esiste uno stato e nessun dato modificabile poiché accetta le funzioni come valori di prima classe e le passa come parametri ad altre funzioni).

PS:comprendere ogni paradigma di programmazione utilizzato dovrebbe chiarire le differenze tra tutti loro.

PS:Alla fine, i paradigmi di programmazione sono solo approcci diversi alla risoluzione dei problemi.

PS: Questo La risposta di quora ha un'ottima spiegazione.

Nessuna delle risposte qui mostra una programmazione funzionale idiomatica.La risposta fattoriale ricorsiva è ottima per rappresentare la ricorsione in FP, ma la maggior parte del codice non è ricorsiva, quindi non penso che la risposta sia pienamente rappresentativa.

Supponiamo che tu abbia un array di stringhe e che ogni stringa rappresenti un numero intero come "5" o "-200".Si desidera verificare questo array di stringhe di input rispetto al caso di test interno (utilizzando il confronto tra numeri interi).Entrambe le soluzioni sono mostrate di seguito

Procedurale

arr_equal(a : [Int], b : [Str]) -> Bool {
    if(a.len != b.len) {
        return false;
    }

    bool ret = true;
    for( int i = 0; i < a.len /* Optimized with && ret*/; i++ ) {
        int a_int = a[i];
        int b_int = parseInt(b[i]);
        ret &= a_int == b_int;  
    }
    return ret;
}

Funzionale

eq = i, j => i == j # This is usually a built-in
toInt = i => parseInt(i) # Of course, parseInt === toInt here, but this is for visualization

arr_equal(a : [Int], b : [Str]) -> Bool =
    zip(a, b.map(toInt)) # Combines into [Int, Int]
   .map(eq)
   .reduce(true, (i, j) => i && j) # Start with true, and continuously && it with each value

Mentre i linguaggi funzionali puri sono generalmente linguaggi di ricerca (poiché al mondo reale piacciono gli effetti collaterali gratuiti), i linguaggi procedurali del mondo reale utilizzeranno la sintassi funzionale molto più semplice quando appropriato.

Questo di solito è implementato con una libreria esterna come Lodash, o disponibile integrato con lingue più recenti come Ruggine.Il lavoro pesante della programmazione funzionale viene svolto con funzioni/concetti come map, filter, reduce, currying, partial, gli ultimi tre dei quali puoi cercare per ulteriore comprensione.

Addendum

Per poter essere utilizzato in natura, il compilatore dovrà normalmente capire come convertire internamente la versione funzionale nella versione procedurale, poiché il sovraccarico delle chiamate di funzione è troppo elevato.Casi ricorsivi come il fattoriale mostrato utilizzeranno trucchi come richiamo della coda per rimuovere l'utilizzo della memoria O(n).Il fatto che non ci siano effetti collaterali consente ai compilatori funzionali di implementare il file && ret ottimizzazione anche quando il .reduce viene fatto per ultimo.L'uso di Lodash in JS ovviamente non consente alcuna ottimizzazione, quindi è un problema per le prestazioni (che di solito non è un problema con lo sviluppo web).Linguaggi come Rust verranno ottimizzati internamente (e avranno funzioni come try_fold assistere && ret ottimizzazione).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top