Programmazione dichiarativa rispetto alla programmazione imperativa

24

Mi sento molto a mio agio con la programmazione Imperativa. Non ho mai avuto problemi nell'esprimere algoritmicamente ciò che voglio fare il computer una volta capito cosa è che voglio che faccia. Ma quando si parla di linguaggi come SQL o mi blocco spesso perché la mia testa è troppo abituata alla programmazione imperativa.

Ad esempio, supponiamo di avere la banda delle relazioni (bandName, bandCountry), venue (venueName, venueCountry), plays (bandName, venueName), e voglio scrivere una query che dice: all venueNames that that for every BandCountry there's una band di quel paese che suona in quel nome.

Esempio: voglio tutti i nomi delle venue in cui le bande dei tutti paesi (bandCountry) hanno suonato. Inoltre, per "relazione" intendo una tabella SQL.

Nella mia mente vado immediatamente "per ogni venueName iterate su tutte le bandCountries e per ogni bandCountry ottieni l'elenco delle band che ne derivano.Se nessuno di loro gioca in venueName, vai al prossimo venueName. l'iterazione di bandCountries aggiunge venueName al set di good venueNames ".

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

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

    
posta EpsilonVector 31.12.2010 - 02:55
fonte

9 risposte

12

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

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

For example, suppose you have the relations band(bandName, bandCountry), venue(venueName, venueCountry), plays(bandName, venueName), and I want to write a query that says: all venueNames such that for every bandCountry there's a band from that country that plays in venue of that name.

Finora, è fantastico. Ma poi fai questo:

In my mind I immediately go "for each venueName iterate over all the bandCountries and for each bandCountry get the list of bands that come from it. If none of them play in venueName, go to next venueName. Else, at the end of the bandCountries iteration add venueName to the set of good venueNames".

In sostanza, stai facendo del lavoro non necessario. Sai che vuoi, che è tutto ciò di cui hai veramente bisogno. Ma poi vai avanti e prova a capire come per averlo.

Se fossi in te, proverei a prendere l'abitudine seguente:

  1. Definisci cosa vuoi.
  2. Consapevolmente fermati dal definire come per averlo.
  3. Scopri come rappresentare ciò che vuoi in SQL.

Ci potrebbe volere un po 'di tempo e impegno da parte tua, ma una volta che si ottiene una programmazione dichiarativa, diventa molto utile. Infatti, potresti trovarti a utilizzare la programmazione dichiarativa nel resto del tuo codice.

Se stai cercando un libro, ti consiglio Teoria SQL e relazionale . Ti aiuta davvero a capire la teoria dietro i database SQL. Ricordati solo di prendere le raccomandazioni di Date con un pizzico di sale. Dà ottime informazioni, ma a volte può essere un po 'supponente.

    
risposta data 31.12.2010 - 03:56
fonte
9

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

all venueNames such that for every bandCountry there's a band from that country that plays in venue of that name

il risultato di questo (se ho compreso correttamente le tue intenzioni!) sarebbe l'insieme di luoghi che hanno almeno una band che suona in quella sede. 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 attuale desiderato è un po 'più complicato. La domanda posta al database è: quali sedi hanno le bande ospitate dai tutti i paesi?

Quindi, definiamo i criteri di appartenenza per un elemento del set desiderato come obiettivo, quindi lavoriamo all'indietro per popolare il set. Un locale è membro del set di output se ha una riga PLAYS per almeno una banda da ogni paese. Come otteniamo questa informazione?

Un modo è contare i paesi distinti per ogni sede e confrontarli 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 di 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 delle fasce, ad es. (in MS-SQL)

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

Possiamo contare i paesi della banda 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 dividere i due insieme 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" rispetto alle variabili temporanee e una sottoquery) ma è abbastanza ovvio quello che stiamo cercando, quindi lasceremo a quello per lo scopo dell'OP.

Lo scopo dell'OP era imparare come spostare la mentalità da imperativa a dichiarativa. A tal fine, guarda cosa stava facendo la soluzione imperativa descritta:

for each venueName iterate over all the bandCountries and for each bandCountry get the list of bands that come from it. If none of them play in venueName, go to next venueName. Else, at the end of the bandCountries iteration add venueName to the set of good venueNames

Quali sono i criteri determinanti di cui sopra? Penso che sia:

...If none of them [the set of bands from a particular country] play in venueName...

Questo è un criterio di squalifica . Il processo di pensiero imperativo inizia con un secchio pieno e butta fuori cose che non si adattano ai criteri. Stiamo filtrando 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 ci sono band di un gruppo musicale che gioca in una sede, la sede è squalificata
  • (parziale) qualificatore: se almeno una banda di un gruppo musicale gioca in una sede, la sede potrebbe essere ok; continua a controllare il resto della bandCountries
  • (completo) qualificatore: se almeno una banda di ogni gruppo musicale gioca in una sede, la sede è qualificata

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

Ora possiamo ragionare attraverso le relazioni tramite la navigazione:

  • inizia con la relazione VENUE [non ce n'è bisogno per la risposta, ma è il punto di partenza concettuale per la navigazione relazionale]
  • unisciti a PLAY su venueName
  • unisciti a BAND su bandName per ottenere il BandCountry
  • non ci interessa il nome della band; seleziona solo venueName e bandCountry
  • non ci preoccupiamo delle ridondanti bandCountries; eliminare i duplicati utilizzando DISTRICT o GROUP BY
  • ci interessa solo il conteggio di bandCountries distinti, non i nomi
  • vogliamo solo luoghi in cui il numero di gruppi distinti è uguale al numero totale di bandCountries

che riporta alla soluzione precedente (o un suo facsimile ragionevole)

SOMMARIO

  • teoria degli insiemi
  • percorsi di navigazione relazionale
  • inclusi vs criteri esclusivi (qualificazione vs squalifica)
risposta data 31.12.2010 - 03:09
fonte
4

Un modo per apprendere come pensare e programmare in uno stile dichiarativo è quello di apprendere un linguaggio di matrice generico come APL o J. SQL probabilmente non è il miglior veicolo per imparare a programmare in modo dichiarativo. In APL o J impari a operare su interi array (vettori, matrici o matrici di rango più alto), senza loop o iterazione espliciti. Questo rende la comprensione di SQL e dell'algebra relazionale molto più facile. 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 valuta un array booleano della stessa forma di V, con 1 che contrassegna i valori che vogliamo mantenere. Non accade all'APLer stagionato che ci sia iterazione in corso, stiamo solo applicando una maschera al vettore V, restituendo un nuovo vettore. Questo è concettualmente ciò che sta facendo un SQL dove clausola o algebra relazionale limitano l'operazione.

Non penso che tu possa avere una buona presa sulla programmazione dichiarativa senza farne molta, e SQL in generale è troppo specifico. È necessario scrivere un sacco di codice generale, imparare come fare senza loop e se / allora / altre strutture e tutti gli apparati che partecipano alla programmazione imperativa, procedurale e in stile scalare.

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

    
risposta data 31.12.2010 - 04:50
fonte
1

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

    
risposta data 31.12.2010 - 03:55
fonte
1

Impari a pensare in modo dichiarativo come se avessi imparato a pensare in modo imperativo: esercitandoti a partire da problemi più semplici e lavorando come "ottieni".

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

Gli stili dichiarativi funzionano con un approccio diverso al quale ti manca l'intuizione per ora (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 ha). 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. Certo, il calcolo delle tuple su cui si basa è tanto dichiarativo quanto in realtà, ma sfortunatamente la purezza del calcolo delle tuple è stata gravemente compromessa dalle realtà dell'implementazione e il linguaggio è diventato, in effetti, un pasticcio confuso. Potresti invece preferire altri linguaggi dichiarativi più direttamente utili (nel senso in cui sei abituato) come the Lisps (in particolare Schema ), Haskell e le ML per ( principalmente) programmazione funzionale o, in alternativa, Prolog e Mercury per (principalmente) programmazione logica.

Imparare queste altre lingue ti darà una migliore comprensione, a mio parere, su come la programmazione dichiarativa funzioni per alcuni motivi:

  1. Sono utili per la programmazione "dalla culla alla tomba", in quanto è possibile 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 questi ti offre una prospettiva diversa sulla programmazione dichiarativa che può darti strade diverse per "ottenerlo" definitivamente.

  3. Ognuno di questi ti dà una prospettiva diversa sul pensare alla programmazione in generale. Miglioreranno la tua capacità di ragionare sui problemi e sulla codifica anche se non li utilizzi mai direttamente.

  4. Le lezioni che imparerai da loro ti aiuteranno anche con il tuo SQL - specialmente se rispondi al calcolo delle tuple dietro i database relazionali per la pura forma di pensare ai dati.

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

    
risposta data 31.12.2010 - 07:51
fonte
1

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

  • Ecco i pezzi di cui ho bisogno.
  • Li metterò insieme in questo modo.
  • Scoprirò ciò che ho appena ottenuto con i seguenti predicati per ottenere quello che sto veramente cercando.

Quanto sopra è un algoritmo imperativo di alto livello e funziona abbastanza bene per me nelle impostazioni SQL. Penso che questo sia considerato un approccio top-down e Steven A. Lowe descrive un buon approccio bottom-up .

    
risposta data 31.12.2010 - 11:00
fonte
1

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

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

    
risposta data 31.12.2010 - 14:51
fonte
1

Mi ci è voluto molto tempo per avvolgere la mia mente anche su SQL. Abbiamo fatto una teoria relazionale all'università e all'epoca, questo è servito solo a complicare le cose. Alla fine, il mio processo di apprendimento è stato ampiamente messo alla prova ed è stato segnalato da vari materiali di apprendimento ed esempi che ho trovato utili lungo il percorso. In sostanza, ci si abitua alla fine e aggiungere un nuovo modo di pensare ai dati e alle query sarà di qualche valore per il tuo sviluppo mentale.

Ho scoperto di essere in grado di accelerare il mio apprendimento creando gradualmente una raccolta di semplici script che dimostrano come utilizzare ciascuna caratteristica linguistica e come ottenere determinati risultati su una tabella nota (le definizioni di tabella sono incluse come riferimento).

All'inizio di quest'anno ho fatto un po 'di formazione formale coinvolgendo un progetto di migrazione dei dati su un database Oracle disordinato, dove ho dovuto mettere insieme i frammenti della mia libreria per filtrare i risultati dell'interrogazione in vari modi fino a quando non avevo esattamente quello che volevo, quindi trasformali come necessario, e così via. Alcune delle query sono diventate molto complesse e difficili da eseguire il debug. Dubito che potrei leggerli ora, ma spero di poter arrivare 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. Sono attualmente in fase di apprendimento di alcuni Haskell, ad esempio, per mantenere e migliorare le mie capacità matematiche.

    
risposta data 31.12.2010 - 16:38
fonte
0

Quando affronti un problema, di solito pensi a come risolverlo. Ma se sai come il computer lo risolve per te! Allora sei preoccupato per in che modo verrà eliminato.

Cerco di dire come succede.

Potresti già avere familiarità con i programmi ricorsivi, nei programmi ricorsivi, tu definisci il problema piuttosto che dire come è risolto. tu definisci la base e definisci n in base a n-1 . (ad esempio factorial(n) = n * factorial(n-1) ) Ma potresti già sapere in che modo 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.

È quello che succede nella programmazione dichiarativa. si definisce tutto in base alle definizioni esistenti. E il computer sa come derivare la risposta per te in base alle funzioni di base.

In SQL potresti non mettere in relazione le definizioni l'una con l'altra ma mettere in relazione gli oggetti o le informazioni tra loro, specificare ciò che si desidera e la ricerca del computer su qualcosa (oggetto, informazioni) in base alle relazioni fornite.

    
risposta data 17.02.2015 - 11:50
fonte

Leggi altre domande sui tag