Programmazione dichiarativa vs. programmazione imperativa


24

Mi sento molto a mio agio con la programmazione imperativa. Non ho mai avuto problemi a esprimere in modo algoritmico ciò che voglio che il computer faccia una volta che ho capito cosa voglio che faccia. Ma quando si tratta di linguaggi come SQL o spesso rimango bloccato perché la mia testa è troppo abituata alla programmazione imperativa.

Ad esempio, supponiamo di avere la banda di relazioni (bandName, bandCountry), la sede (venueName, venueCountry), i suoni (bandName, venueName) e voglio scrivere una query che dice: tutti i nomi dei locali in modo tale che per ogni bandCountry ci sia una band di quel paese che gioca al posto di quel nome.

Esempio: voglio tutti i nomi dei locali in cui suonano band di tutti i paesi (bandCountry). Inoltre, per "relazione" intendo una tabella SQL.

Nella mia mente vado immediatamente "per ogni locale Nome che scorre su tutta la bandCountries e per ogni bandCountry ottengo l'elenco delle band che provengono da esso. Se nessuno di loro suona in locationName, vai al prossimo locationName. Altrimenti, alla fine della bandCountries iteration aggiungi venueName all'insieme di buoni locationNames ".

... ma non puoi parlare così in SQL e in realtà ho bisogno di pensare a come formularlo, con l'intuitiva soluzione imperativa che mi assilla costantemente nella parte posteriore della testa. Qualcun altro ha avuto questo problema? Come hai superato questo? Hai capito un cambio di paradigma? Hai creato una mappa dai concetti imperativi ai concetti SQL per tradurre le soluzioni imperative in soluzioni dichiarative? Leggi un buon libro?

PS Non sto cercando una soluzione alla query sopra, l'ho risolta.


1
Questa è una buona domanda perché stai esprimendo una debolezza che molti (incluso me stesso) hanno.
David Weiser, il

Potrebbe essere utile definire cosa intendi per "relazione" nella tua domanda. Nel modello relazionale (la matematica dietro SQL), "relazione" è approssimativamente analoga a una tabella SQL. Molte persone diranno "relazione" quando intendono davvero dire "relazione".
Jason Baker,

Impara la teoria degli insiemi e la matematica discreta.

1
@ Jase21, personalmente conosco abbastanza bene entrambi, ma le cose non banali in SQL sono ancora divertenti. Nessuno degli esempi matematici chiari riguarda le strane cose del mondo reale. Inoltre, è possibile utilizzare LINQ e quindi non essere disturbati con SQL. Infine, per chi chiede: ti abituerai con il tempo.
Giobbe

Risposte:


12

L'idea alla base di fare le cose in modo dichiarativo è che dovresti specificare cosa , non come .

Per me, sembra che tu sia sulla buona strada. Il problema non è che stai pensando alle cose nel modo sbagliato. È che stai andando troppo lontano. Diamo un'occhiata a cosa stai cercando di fare:

Ad esempio, supponiamo di avere la banda di relazioni (bandName, bandCountry), la sede (venueName, venueCountry), i suoni (bandName, venueName) e voglio scrivere una query che dice: tutti i nomi dei locali in modo tale che per ogni bandCountry ci sia una band di quel paese che gioca al posto di quel nome.

Finora è fantastico. Ma poi fai questo:

Nella mia mente vado immediatamente "per ogni locale Nome che scorre su tutta la bandCountries e per ogni bandCountry ottengo l'elenco delle band che provengono da esso. Se nessuno di loro suona in locationName, vai al prossimo locationName. Altrimenti, alla fine della bandCountries iteration aggiungi venueName all'insieme di buoni locationNames ".

In sostanza, stai facendo un lavoro inutile. Sai cosa vuoi, che è tutto ciò di cui hai veramente bisogno. Ma poi vai avanti e cerchi di capire come ottenerlo.

Se fossi in te, proverei a prendere la seguente abitudine:

  1. Definisci ciò che vuoi.
  2. Consapevolmente impedirti di definire come ottenerlo.
  3. Scopri come rappresentare ciò che desideri in SQL.

Potrebbero volerci un po 'di tempo e sforzi da parte tua, ma una volta che hai davvero sviluppato la programmazione dichiarativa, diventa molto utile. In effetti, potresti trovarti a utilizzare la programmazione dichiarativa nel resto del codice.

Se stai cercando un libro, consiglierei SQL e la teoria relazionale . Ti aiuta davvero a capire la teoria alla base dei database SQL. Ricorda solo di prendere i consigli di Date con un granello di sale. Fornisce ottime informazioni, ma a volte può essere un po 'supponente.


Non capisco come capire come ottenere qualcosa sia l'approccio sbagliato. Non importa che tipo di linguaggio stai usando devi capire come dirgli di fare quello che vuoi.
davidk01,

9

pensare in termini di set, non di iteratori; le istruzioni sql definiscono le proprietà del set di output desiderato (aka tabella / relazione)

tutti i nomi dei locali in modo tale che per ogni band Paese ci sia una band di quel paese che suona in quel luogo

il risultato di questo (se ho capito bene le tue intenzioni!) sarebbe l'insieme di locali che hanno almeno una band che suona in quel locale. L'iterazione su bandCountry non è necessaria, poiché la relazione PLAYS ha già le informazioni che cerchi, devi solo eliminare i duplicati

quindi in SQL questo sarebbe:

select 
    distinct venueName
from PLAYS

EDIT: ok, quindi il set effettivo desiderato è un po 'più complicato. La domanda che viene posta al database è: quali locali hanno ospitato band di tutti i paesi?

Quindi, definiamo i criteri di appartenenza per un elemento dell'insieme desiderato come obiettivo, quindi lavoriamo all'indietro per popolare l'insieme. Una sede è un membro del set di output se ha una riga PLAYS per almeno una band di ogni paese. Come otteniamo queste informazioni?

Un modo è contare i paesi distinti per ciascuna sede e confrontarlo con il conteggio di tutti i paesi. Ma non abbiamo una relazione COUNTRY. Se pensiamo al modello dato per un momento, vediamo che l'insieme di tutti i paesi non è il criterio giusto; è l'insieme di tutti i paesi che hanno almeno una banda. Quindi non abbiamo bisogno di una tabella dei paesi (anche se per un modello normalizzato dovremmo averne uno) e non ci interessa il paese della sede, possiamo semplicemente contare i paesi che hanno bande, ad esempio (in MS-SQL )

declare @BandCountryCount int
select
    @BandCountryCount = COUNT(distinct bandCountry)
from BAND

Possiamo contare i paesi della band per ogni sede

select
    P.venueName, COUNT(distinct B.bandCountry) as VenueBandCountryCount
from PLAYS P
    inner join BAND B on B.bandName = P.bandName

e possiamo mettere insieme i due usando una sottoquery

select
    venueName
from (
    select
        P.venueName, COUNT(distinct B.bandCountry) as VenueBandCountryCount
    from PLAYS P
        inner join BAND B on B.bandName = P.bandName
) X
where X.VenueBandCountryCount = @BandCountryCount

Ora, questa non è la query più carina possibile (GROUP BY e HAVING potrebbero essere considerati una soluzione più "elegante" delle variabili temporanee e una subquery) ma è abbastanza ovvio ciò che stiamo cercando, quindi lo lasceremo allo scopo del PO .

Lo scopo del PO era quello di imparare a spostare la mentalità dall'imperativo al dichiarativo. A tal fine, guarda cosa stava facendo la soluzione imperativa descritta:

per ogni sede, dai un nome a tutte le bandCountries e per ogni bandCountry ottieni l'elenco delle band che ne derivano. Se nessuno di loro gioca nel nome del locale, vai al nome del luogo successivo. Altrimenti, alla fine della band, l'iterazione di Countuntries aggiunge venueName all'insieme di good venueNames

Quali sono i criteri determinanti in quanto sopra? Io penso che sia:

... Se nessuno di loro [l'insieme di band di un determinato paese] suona nel locale Nome ...

Questo è un criterio squalificante . Il processo di pensiero imperativo sta iniziando con un secchio pieno e gettando via cose che non soddisfano i criteri. Stiamo filtrando i dati.

Va bene per cose semplici, ma aiuta a pensare in termini di costruzione del set di risultati desiderato; quali sono i criteri di qualificazione corrispondenti che consentirebbero invece di riempire il secchio?

  • squalificatore: se non esiste una band di una band Country che suona in una sede, la sede è squalificata
  • qualificatore (parziale): se almeno una band di una band Country suona in una sede, la sede potrebbe essere ok; continua a controllare il resto della bandCountries
  • qualificatore (completo): se almeno una band di ogni band Country suona in una sede, allora la sede è qualificata

Il qualificatore finale può essere semplificato usando i conteggi: una bandCountry è 'soddisfatta' se almeno una band da lì suona in un locale; il numero di paesi della band "soddisfatti" per una sede deve essere uguale al numero di paesi della band per la sede da qualificare.

Ora possiamo ragionare attraverso le relazioni tramite la navigazione:

  • inizia con la relazione VENUE [non ne abbiamo bisogno per la risposta, ma è il punto di partenza concettuale per la navigazione relazionale]
  • iscriviti a PLAYS in venueName
  • unisciti a BAND su bandName per ottenere bandCountry
  • non ci interessa il nome della band; seleziona solo la sede Nome e banda Paese
  • non ci importa di BandCountries ridondanti; eliminare i duplicati utilizzando DISTRICT o GROUP BY
  • ci preoccupiamo solo del conteggio di BandCountries distinti, non dei nomi
  • vogliamo solo locali in cui il conteggio di bandCountries distinti è uguale al numero totale di bandCountries

che riporta alla soluzione sopra (o ad un ragionevole facsimile)

SOMMARIO

  • insiemistica
  • percorsi di navigazione relazionale
  • criteri inclusivi vs esclusivi (qualificazioni vs squalifiche)

In realtà è "un insieme di locali in cui suonavano band di tutti i paesi (bandCountry> = venueCountry)".
Epsilon

@EpsilonVector: vedi le modifiche
Steven A. Lowe,

4

Un modo per imparare a pensare e programmare in uno stile dichiarativo è quello di imparare un linguaggio array generico come APL o J. SQL non è probabilmente il miglior veicolo per imparare a programmare in modo dichiarativo. In APL o J impari a operare su interi array (vettori, matrici o array di rango superiore), senza loop o iterazioni esplicite. Ciò rende molto più semplice la comprensione di SQL e dell'algebra relazionale. Come esempio molto semplice, per selezionare elementi da un vettore V il cui valore è maggiore di 100, in APL scriviamo:

(V>100)/V

Qui V> 100 restituisce una matrice booleana della stessa forma di V, con 1 che indica i valori che vogliamo mantenere. All'APLer esperto non accade che ci sia iterazione in corso, stiamo solo applicando una maschera al vettore V, restituendo un nuovo vettore. Questo ovviamente è concettualmente ciò che sta facendo un SQL in cui clausola o algebra relazionale limitano l'operazione.

Non penso che tu possa avere una buona conoscenza della programmazione dichiarativa senza farne molta e SQL in generale è troppo specifico. Devi scrivere un sacco di codice generico, imparando a fare a meno dei loop e delle strutture if / then / else e di tutto l'apparato che frequenta una programmazione imperativa, procedurale e scalare.

Potrebbero esserci anche altri linguaggi funzionali che aiutano in questo modo di pensare, ma i linguaggi dell'array sono molto vicini a SQL.


+1 per "[non puoi] avere una buona presa ... senza fare molto". Nessuno ha imparato la programmazione imperativa (con i suoi costrutti chiaramente contro-intuitivi come a = a + 1) durante la notte. Ci vuole tempo per apprendere stili dichiarativi come la logica, il funzionamento, ecc. Proprio come ci è voluto del tempo per imparare la programmazione imperativa.
SOLO IL MIO OPINIONE corretta il

1

Innanzitutto, devi imparare entrambi. Potresti avere una preferenza, ma quando lavori in aree in cui l'altro è meglio, non combatterlo. Molti programmatori sono tentati di usare i cursori nei database relazionali poiché sono così abituati a passare da un record all'altro, ma il database è molto meglio negli insiemi. Non vuoi entrare nella mentalità di "So come fare in questo modo e ho il massimo controllo, blah, blah, blah".


1

Impari a pensare in modo dichiarativo come hai imparato a pensare in modo imperativo: dalla pratica a partire da problemi più semplici e lavorando su come "capisci".

Le tue prime esperienze con la programmazione imperativa includevano un sacco di affermazioni contro-intuitive (e, in effetti, assolutamente ridicole) come " a = a + 1". Hai avvolto la tua mente al punto che ora probabilmente non ricordi nemmeno il rinculo dall'ovvia falsità dell'affermazione. Il tuo problema con gli stili dichiarativi è che sei tornato dove eri quando hai iniziato con gli stili imperativi: un "newb clueless". Ancora peggio, hai anni di pratica con uno stile che è assolutamente in contrasto con questo nuovo stile e hanno anni di abitudini da annullare - come l'abitudine di "controllare a tutti i costi".

Gli stili dichiarativi funzionano con un approccio diverso che per ora ti manca l'intuizione (a meno che tu non abbia mantenuto le tue abilità matematiche molto acute nel corso degli anni - cosa che la maggior parte delle persone non fa). Devi imparare di nuovo a pensare e l'unico modo per imparare di nuovo è farlo, un semplice passo alla volta.

Scegliere SQL come prima incursione nella programmazione dichiarativa potrebbe essere un errore se si vuole davvero imparare i concetti. Sicuramente il calcolo della tupla su cui si basa è davvero dichiarativo, ma sfortunatamente la purezza del calcolo della tupla è stata gravemente compromessa dalle realtà dell'implementazione e il linguaggio è, in effetti, diventato un po 'confuso. Potresti invece voler guardare altri linguaggi dichiarativi più direttamente utili (nel senso in cui sei abituato) come Lisps (esp. Scheme ), Haskell e le ML per la programmazione (principalmente) funzionale o, in alternativa, Prolog e Mercury per (principalmente) programmazione logica.

Imparare queste altre lingue ti darà una visione migliore, secondo me, di come funziona la programmazione dichiarativa per alcuni motivi:

  1. Sono utili per la programmazione "dalla culla alla tomba" - come in te puoi scrivere un programma completo in queste lingue dall'inizio alla fine. Sono utili da soli, a differenza di SQL che è davvero abbastanza inutile per la maggior parte delle persone come linguaggio autonomo.

  2. Ognuno di essi ti dà una diversa inclinazione nella programmazione dichiarativa che può darti strade diverse per "ottenerlo" finalmente.

  3. Ognuno di essi ti dà anche una diversa inclinazione a pensare alla programmazione in generale. Miglioreranno la tua capacità di ragionare su problemi e codifica anche se non li usi mai direttamente da solo.

  4. Le lezioni che imparerai da loro ti aiuteranno anche con il tuo SQL, specialmente se rispolvererai il calcolo della tupla dietro i database relazionali per la pura forma di pensiero sui dati.

Consiglio in particolare di imparare uno dei linguaggi funzionali ( Clojure , come uno dei Lisps, è probabilmente una buona scelta qui) e uno dei linguaggi logici (mi piace di più Mercury, ma Prolog ha molto più materiale utile per l'apprendimento) per la massima espansione del processo di pensiero.


1

Non è sbagliato pensare in modo imperativo in un'impostazione dichiarativa come SQL. È solo che il pensiero imperativo dovrebbe avvenire a un livello leggermente più alto di quello che hai descritto. Ogni volta che devo interrogare un database che utilizza SQL, penso sempre a me stesso:

  • Ecco i pezzi di cui ho bisogno.
  • Li metterò insieme in questo modo.
  • Ho intenzione di sminuire ciò che ho appena ottenuto con i seguenti predicati per arrivare a quello che sto davvero cercando.

Quanto sopra è un algoritmo imperativo di alto livello e funziona abbastanza bene per me nell'impostazione SQL. Penso che questo sia considerato un approccio dall'alto verso il basso e Steven A. Lowe descrivono un approccio dal basso piuttosto buono .


1

La chiave della tua domanda è in quello che hai detto nel penultimo paragrafo: "Non puoi parlare così in SQL". In questa fase può essere più utile avvicinarsi a SQL come lingua straniera anziché come linguaggio di programmazione. Se la pensi in questo modo, scrivere una query SQL significa davvero tradurre in inglese "SQLish" ciò che desideri. Il computer capisce perfettamente SQLish e farà esattamente quello che dici, quindi non devi preoccuparti dell'implementazione finché traduci correttamente.

Detto questo, qual è il modo migliore per imparare una lingua straniera? Ovviamente devi imparare la grammatica e il vocabolario, che puoi ottenere dalla tua documentazione SQL. La cosa più importante è la pratica. Dovresti leggere e scrivere più SQL che puoi e non sentire di dover prima conoscere la sintassi; puoi e dovresti cercare le cose mentre procedi. Saprai che ce l'hai quando trovi più facile descrivere quali dati vuoi in SQL che in inglese.


1

Mi ci è voluto molto tempo per avvolgere la mia testa anche su SQL. Abbiamo fatto una teoria relazionale all'università e all'epoca, che serviva solo a complicare le cose. Alla fine, il mio processo di apprendimento è stato molto tentativo ed informato da vari materiali di apprendimento ed esempi che ho trovato utili lungo la strada. In sostanza, alla fine ti abituerai e aggiungere un nuovo modo di pensare ai dati e alle domande sarà di qualche valore per il tuo sviluppo mentale.

Ho scoperto che sono stato in grado di accelerare il mio apprendimento creando gradualmente una raccolta di semplici script che dimostrano come utilizzare ciascuna funzionalità della lingua e come ottenere determinati risultati su una tabella nota (definizioni di tabella incluse come riferimento).

All'inizio di quest'anno ho svolto un corso di formazione formale su un progetto di migrazione dei dati su un disordinato database Oracle, in cui ho dovuto mettere gradualmente insieme i frammenti della mia libreria per filtrare i risultati della query in vari modi fino a quando non avevo esattamente quello che volevo, quindi trasformarli come necessario e così via. Alcune delle query sono diventate molto complesse e difficili da eseguire il debug. Dubito di poterli leggere ora, ma spero di poter arrivare di nuovo a una soluzione simile usando i miei blocchi di riferimento.

Altri modi per aumentare la consapevolezza intuitiva degli spazi dichiarativi e funzionali sono l'apprendimento della teoria degli insiemi e dei linguaggi di programmazione più adatti a un particolare paradigma. Attualmente sto imparando un po 'di Haskell, ad esempio, per mantenere e migliorare le mie capacità matematiche.


0

Quando affronti un problema, di solito pensi a come risolverlo. Ma se sai come il computer lo risolve per te! Quindi ti preoccupi di come verrà eliminato.

Provo a dire come succede.

Potresti già avere familiarità con i programmi ricorsivi, nei programmi ricorsivi, definisci il problema piuttosto che dire come è risolto. si definisce la base e si definisce n in base a n-1 . (ad esempio factorial(n) = n * factorial(n-1)) Ma potresti già sapere come il computer lo risolve. inizia dalla funzione e chiama la funzione in modo ricorsivo fino a raggiungere una definizione di base, quindi valuta tutte le altre funzioni in base al valore di base.

È ciò che accade nella programmazione dichiarativa. si definisce tutto in base alle definizioni esistenti. E il computer sa come ottenere la risposta per te in base alle funzioni di base.

In SQL potresti non mettere in relazione le definizioni tra loro, ma devi mettere in relazione oggetti o informazioni tra loro, specificare ciò che desideri e la ricerca del computer con qualcosa (oggetto, informazioni) in base alle relazioni che hai fornito.

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.