Deklarative Programmierung vs. Imperative Programmierung


24

Ich fühle mich sehr wohl mit imperativer Programmierung. Ich habe nie Probleme, algorithmisch auszudrücken, was der Computer tun soll, wenn ich herausgefunden habe, was er tun soll. Aber wenn es um Sprachen wie SQL geht oder ich oft nicht weiterkomme, weil mein Kopf zu sehr an Imperative Programmierung gewöhnt ist.

Angenommen, Sie haben die Beziehung band (bandName, bandCountry), venue (venueName, venueCountry), plays (bandName, venueName) und ich möchte eine Abfrage schreiben, die besagt: all venueNames, sodass für jedes bandCountry eine Band von vorhanden ist das Land, das in diesem Namen spielt.

Beispiel: Ich möchte alle venueNames, in denen Bands aus allen Ländern (bandCountry) gespielt haben. Mit "Relation" meine ich auch eine SQL-Tabelle.

In meinen Gedanken gehe ich sofort für jeden VenueName über alle BandCountries und bekomme für jedes BandCountry die Liste der Bands, die daraus stammen. Wenn keiner von ihnen in VenueName spielt, gehe zum nächsten VenueName. Sonst am Ende der BandCountries Iteration add venueName to the set of good venueNames ".

... aber so kann man in SQL nicht sprechen, und ich muss tatsächlich darüber nachdenken, wie ich das formulieren soll, wobei die intuitive Imperativlösung ständig im Hinterkopf nörgelt. Hatte noch jemand dieses Problem? Wie haben Sie das überwunden? Haben Sie einen Paradigmenwechsel herausgefunden? Erstellen Sie eine Karte von imperativen Konzepten zu SQL-Konzepten, um imperative Lösungen in deklarative zu übersetzen? Ein gutes Buch lesen?

PS Ich bin nicht auf der Suche nach einer Lösung für die obige Frage, ich habe es gelöst.


1
Dies ist eine gute Frage, weil Sie eine Schwäche ausdrücken, die viele (einschließlich ich) haben.
David Weiser

Es kann nützlich sein, in Ihrer Frage zu definieren, was Sie unter "Beziehung" verstehen. Im relationalen Modell (die Mathematik hinter SQL) ist "relation" ungefähr analog zu einer SQL-Tabelle. Viele Leute werden "Beziehung" sagen, wenn sie wirklich "Beziehung" sagen wollen.
Jason Baker

Lerne Mengenlehre und diskrete Mathematik.

1
@ Jase21, ich persönlich kenne mich mit beiden ziemlich gut aus, aber nicht triviale Dinge in SQL fühlen sich immer noch lustig an. Keines der sauberen Mathematikbeispiele befasst sich mit den seltsamen Dingen der realen Welt. Darüber hinaus kann man LINQ verwenden und ist daher nicht mit SQL belästigt. Zum Schluss an den Fragesteller: Sie werden sich mit der Zeit daran gewöhnen.
Job

Antworten:


12

Die Idee, die dahinter steckt, ist, dass Sie genau angeben müssen, was und nicht wie .

Für mich klingt es so, als wären Sie auf dem richtigen Weg. Das Problem ist nicht, dass Sie die Dinge falsch denken. Es ist so, dass du zu weit gehst. Schauen wir uns an, was Sie versuchen:

Angenommen, Sie haben die Beziehung band (bandName, bandCountry), venue (venueName, venueCountry), plays (bandName, venueName) und ich möchte eine Abfrage schreiben, die besagt: all venueNames, sodass für jedes bandCountry eine Band von vorhanden ist das Land, das in diesem Namen spielt.

Bisher ist das großartig. Aber dann machst du das:

In meinen Gedanken gehe ich sofort für jeden VenueName über alle BandCountries und bekomme für jedes BandCountry die Liste der Bands, die daraus stammen. Wenn keiner von ihnen in VenueName spielt, gehe zum nächsten VenueName. Sonst am Ende der BandCountries Iteration add venueName to the set of good venueNames ".

Im Wesentlichen erledigen Sie unnötige Arbeit. Sie wissen, was Sie wollen, und das ist alles, was Sie wirklich brauchen. Aber dann fahren Sie fort und versuchen herauszufinden, wie Sie es bekommen.

Wenn ich Sie wäre, würde ich versuchen, die folgende Angewohnheit zu bekommen:

  1. Definieren Sie, was Sie wollen.
  2. Halte dich bewusst davon ab zu definieren, wie du es bekommst.
  3. Finden Sie heraus, wie Sie die gewünschten Elemente in SQL darstellen können.

Es mag einige Zeit und Mühe dauern, aber sobald Sie die deklarative Programmierung wirklich beherrschen, wird es sehr nützlich. Tatsächlich verwenden Sie im Rest Ihres Codes möglicherweise deklaratives Programmieren.

Wenn Sie nach einem Buch suchen, empfehle ich SQL und Relationale Theorie . Es hilft Ihnen wirklich, die Theorie hinter SQL-Datenbanken zu verstehen. Denken Sie daran, die Empfehlungen von Date mit einem Körnchen Salz zu nehmen. Er gibt sehr gute Informationen, aber er kann manchmal ein bisschen einschätzen.


Ich verstehe nicht, wie falsch es ist, herauszufinden, wie man an etwas kommt. Es spielt keine Rolle, welche Art von Sprache Sie verwenden, sondern Sie müssen herausfinden, wie Sie sie anweisen können, das zu tun, was Sie wollen.
Davidk01

9

Denken Sie in Mengen, nicht in Iteratoren. Die SQL-Anweisungen definieren die Eigenschaften der gewünschten Ausgabemenge (auch bekannt als Tabelle / Relation).

alle venueNames so, dass es für jedes bandCountry eine Band aus diesem Land gibt, die am Veranstaltungsort dieses Namens spielt

das ergebnis davon (wenn ich deine absichten richtig verstanden habe!) wären die veranstaltungsorte, an denen mindestens eine band spielt. Die Iteration über bandCountry ist unnötig, da die PLAYS-Beziehung bereits die gesuchten Informationen enthält. Sie müssen nur die Duplikate entfernen

In SQL wäre dies also:

select 
    distinct venueName
from PLAYS

EDIT: ok, also das eigentlich gewünschte set ist etwas komplizierter. Die Frage, die der Datenbank gestellt wird, lautet: Welche Veranstaltungsorte haben Bands aus allen Ländern beherbergt ?

Also definieren wir die Mitgliedschaftskriterien für ein Element der gewünschten Menge als Ziel und arbeiten dann rückwärts, um die Menge zu füllen. Ein Veranstaltungsort ist Mitglied des Output-Sets, wenn es eine PLAYS-Zeile für mindestens eine Band aus jedem Land gibt. Wie erhalten wir diese Informationen?

Eine Möglichkeit besteht darin, die verschiedenen Länder für jeden Veranstaltungsort zu zählen und mit der Anzahl aller Länder zu vergleichen. Wir haben aber keine LAND-Beziehung. Wenn wir für einen Moment über das gegebene Modell nachdenken, sehen wir, dass die Menge aller Länder nicht die richtigen Kriterien sind; Es ist die Menge aller Länder, die mindestens eine Band haben. Wir brauchen also keine Ländertabelle (obwohl wir für ein normalisiertes Modell eine haben sollten), und wir kümmern uns nicht um das Land des Veranstaltungsortes, wir können nur die Länder zählen, die Bands haben, z. B. (in MS-SQL) )

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

Wir können die Bandländer für jeden Veranstaltungsort zählen

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

und wir können die beiden unter Verwendung einer Unterabfrage zusammenfügen

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

Nun, das ist nicht die schönste mögliche Abfrage (GROUP BY und HAVING können als elegantere Lösung angesehen werden als temporäre Variablen und Unterabfragen), aber es ist ziemlich offensichtlich, wonach wir suchen .

Ziel des OP war es, zu lernen, wie die Denkweise von imperativ zu deklarativ geändert werden kann. Schauen Sie sich zu diesem Zweck an, was die beschriebene Imperativlösung tat:

für jeden venueName werden alle bandCountries durchlaufen und für jedes bandCountry wird die Liste der davon stammenden Bands angezeigt. Wenn keiner von ihnen in venueName spielt, gehe zum nächsten venueName. Andernfalls wird am Ende der Iteration von bandCountries venueName zum Satz guter venueNames hinzugefügt

Was ist das bestimmende Kriterium in den oben genannten? Ich denke, es ist:

... Wenn keine von ihnen [die Gruppe von Bands aus einem bestimmten Land] in venueName spielt ...

Dies ist ein Disqualifikationskriterium . Der imperative Denkprozess beginnt mit einem vollen Eimer und dem Wegwerfen von Dingen, die nicht den Kriterien entsprechen. Wir filtern Daten heraus.

Für einfache Dinge ist das in Ordnung, aber es hilft, die gewünschte Ergebnismenge zu konstruieren. Was sind die entsprechenden Qualifikationskriterien , anhand derer man stattdessen den Eimer füllen kann?

  • Disqualifier: Wenn es keine Band aus einem Bandland gibt, die an einem Veranstaltungsort spielt, wird der Veranstaltungsort disqualifiziert
  • (Teil-) Qualifier: Wenn mindestens eine Band aus einem bandCountry an einem Veranstaltungsort spielt, ist der Veranstaltungsort möglicherweise in Ordnung. Überprüfe weiterhin den Rest der BandCountries
  • (Full) Qualifier: Wenn mindestens eine Band aus jedem BandCountry an einem Veranstaltungsort spielt, ist der Veranstaltungsort qualifiziert

Das finale Qualifikationsspiel kann durch Zählungen vereinfacht werden: Ein bandCountry ist "zufrieden", wenn mindestens eine Band von dort an einem Veranstaltungsort spielt; Die Anzahl der "zufriedenen" Bandländer für einen Veranstaltungsort muss der Anzahl der Bandländer entsprechen, in denen sich der Veranstaltungsort qualifiziert.

Jetzt können wir durch Navigation über Beziehungen hinweg argumentieren:

  • beginne mit der VENUE-Relation [wir brauchen sie nicht für die Antwort, aber sie ist der konzeptionelle Ausgangspunkt für die relationale Navigation]
  • trete PLAYS auf venueName bei
  • trete BAND auf bandName bei, um das bandCountry zu erhalten
  • Der Bandname ist uns egal. wähle nur den venueName und das bandCountry
  • redundante bandCountries interessieren uns nicht; Entfernen Sie doppelte Einträge mit DISTRICT oder GROUP BY
  • Wir kümmern uns nur um die Anzahl der verschiedenen Band-Länder, nicht um die Namen
  • Wir wollen nur Veranstaltungsorte, bei denen die Anzahl der verschiedenen Bandländer der Gesamtzahl der Bandländer entspricht

Dies führt zurück zu der obigen Lösung (oder einem vernünftigen Faksimile davon).

ZUSAMMENFASSUNG

  • Mengenlehre
  • relationale Navigationspfade
  • inklusive vs. exklusive Kriterien (Qualifikation vs. Disqualifikation)

Es ist eigentlich "eine Reihe von Veranstaltungsorten, an denen Bands aus allen Ländern (bandCountry> = venueCountry) spielten".
EpsilonVector

@EpsilonVector: siehe Änderungen
Steven A. Lowe

4

Eine Möglichkeit zu lernen, wie man deklarativ denkt und programmiert, ist das Erlernen einer Allzweck-Array-Sprache wie APL oder J. SQL ist wahrscheinlich nicht das beste Mittel, um deklarativ programmieren zu lernen. In APL oder J lernen Sie, ganze Arrays (Vektoren, Matrizen oder Arrays mit höherem Rang) ohne explizite Schleife oder Iteration zu bearbeiten. Dies erleichtert das Verständnis von SQL und relationaler Algebra erheblich. Als ein sehr einfaches Beispiel, um Elemente aus einem Vektor V auszuwählen, deren Wert größer als 100 ist, schreiben wir in APL:

(V>100)/V

Hier wird V> 100 zu einem booleschen Array mit der gleichen Form wie V ausgewertet, wobei Einsen die Werte markieren, die beibehalten werden sollen. Es fällt dem erfahrenen APLer nicht ein, dass eine Iteration stattfindet, wir wenden lediglich eine Maske auf den Vektor V an und geben einen neuen Vektor zurück. Dies ist natürlich konzeptionell das, was eine SQL-Where-Klausel oder eine Operation zur Einschränkung der relationalen Algebra bewirkt.

Ich glaube nicht, dass man deklarative Programmierung ohne viel Aufwand in den Griff bekommen kann, und SQL ist im Allgemeinen zu spezifisch. Sie müssen eine Menge Code für allgemeine Zwecke schreiben und lernen, wie man ohne Schleifen und if / then / else-Strukturen und alle Geräte auskommt, die sich mit imperativer, prozeduraler und skalarer Programmierung befassen.

Es mag auch andere funktionale Sprachen geben, die bei dieser Denkweise hilfreich sind, aber die Array-Sprachen sind SQL sehr ähnlich.


+1 für "[Sie können nicht] einen guten Griff bekommen ... ohne viel davon zu tun". Auch die imperative Programmierung (mit ihren eindeutig kontraintuitiven Konstrukten wie a = a + 1) hat niemand über Nacht gelernt . Es braucht Zeit, um deklarative Stile wie Logik, Funktionalität usw. zu lernen, genau wie es Zeit brauchte, um imperative Programmierung zu lernen.
NUR MEINE STELLUNGNAHME

1

Zuerst muss man beides lernen. Möglicherweise haben Sie eine Präferenz, aber wenn Sie in Bereichen arbeiten, in denen die andere besser ist, bekämpfen Sie sie nicht. Viele Programmierer sind versucht, Cursor in relationalen Datenbanken zu verwenden, da sie es gewohnt sind, durch jeden Datensatz zu navigieren, aber die Datenbank ist in Mengen viel besser. Du willst nicht in die Denkweise von "Ich weiß, wie man das macht und ich habe die meiste Kontrolle, bla, bla, bla" geraten.


1

Sie lernen deklarativ zu denken, wie Sie es gelernt haben: indem Sie mit einfacheren Problemen beginnen und so weitermachen, wie Sie es "verstanden" haben.

Ihre ersten Erfahrungen mit der imperativen Programmierung beinhalteten eine ganze Reihe von kontraintuitiven (und in der Tat äußerst lächerlichen) Aussagen wie " a = a + 1". Sie haben Ihre Gedanken dahingehend verschlungen, dass Sie sich jetzt wahrscheinlich nicht mehr an den Rückzug aus der offensichtlichen Unwahrheit der Aussage erinnern. Ihr Problem mit deklarativen Stilen ist, dass Sie wieder da sind, wo Sie waren, als Sie mit imperativen Stilen begannen: ein "ahnungsloser Anfänger". Schlimmer noch, Sie haben jahrelange Erfahrung mit einem Stil, der mit diesem neuen Stil völlig im Widerspruch steht, und haben jahrelange Gewohnheiten, die Sie rückgängig machen müssen - wie die Gewohnheit, "um jeden Preis zu kontrollieren".

Deklarative Stile funktionieren mit einer anderen Herangehensweise, die Ihnen im Moment fehlt (es sei denn, Sie haben Ihre mathematischen Fähigkeiten über die Jahre hinweg sehr gut gepflegt - was die meisten Menschen nicht tun). Sie müssen neu lernen, wie man denkt, und die einzige Möglichkeit, dies neu zu lernen, besteht darin, es Schritt für Schritt zu tun.

Wenn Sie die Konzepte wirklich lernen möchten, ist es möglicherweise ein Fehler, SQL als ersten Versuch zur deklarativen Programmierung zu wählen. Sicher, der Tupel-Kalkül, auf dem es basiert, ist so aussagekräftig wie es nur geht, aber leider wurde die Reinheit des Tupel-Kalküls durch die Realitäten der Implementierung stark beeinträchtigt und die Sprache ist praktisch ein bisschen durcheinander geraten. Vielleicht möchten Sie stattdessen andere direktere (in dem Sinne, wie Sie es gewohnt sind) deklarative Sprachen wie Lisps (besonders Schema ), Haskell und die MLs für (größtenteils) funktionale Programmierung oder alternativ Prolog und Mercury für ansehen (meistens) logische Programmierung.

Wenn Sie diese anderen Sprachen lernen, erhalten Sie meiner Meinung nach einen besseren Einblick in die Funktionsweise der deklarativen Programmierung, und zwar aus folgenden Gründen:

  1. Sie sind nützlich für die "Cradle to Grave" -Programmierung - da Sie von Anfang bis Ende ein vollständiges Programm in diesen Sprachen schreiben können. Sie sind im Gegensatz zu SQL, das für die meisten Menschen als eigenständige Sprache völlig nutzlos ist, nützlich.

  2. Sie geben Ihnen jeweils eine andere Sichtweise der deklarativen Programmierung, die Ihnen verschiedene Wege aufzeigen kann, um es endlich zu "verstehen".

  3. Sie geben Ihnen auch jeweils eine andere Neigung, über das Programmieren im Allgemeinen nachzudenken. Sie verbessern Ihre Fähigkeit, über Probleme und Codierungen nachzudenken, selbst wenn Sie sie nie direkt verwenden.

  4. Die Lektionen, die Sie daraus lernen, helfen Ihnen auch bei Ihrer SQL - insbesondere, wenn Sie die Tupelrechnung hinter relationalen Datenbanken auffrischen, um die reine Form des Denkens über Daten zu erhalten.

Ich würde besonders empfehlen, eine der funktionalen Sprachen ( Clojure als eine der Lisps ist hier wahrscheinlich eine gute Wahl) und eine der logischen Sprachen (Mercury gefällt mir am besten, aber Prolog hat viel mehr nützliches Material zum Lernen) zu lernen. für maximale gedanken prozessexpansion.


1

Es ist nicht falsch, in einer deklarativen Umgebung wie SQL unbedingt zu denken. Es ist nur so, dass das imperative Denken auf einer etwas höheren Ebene stattfinden sollte als von Ihnen beschrieben. Wann immer ich eine Datenbank abfragen muss, die SQL verwendet, denke ich immer bei mir:

  • Hier sind die Teile, die ich brauche.
  • Ich werde sie auf diese Weise zusammenstellen.
  • Ich werde das, was ich gerade mit den folgenden Prädikaten erhalten habe, abschneiden, um zu dem zu gelangen, wonach ich wirklich suche.

Das obige ist ein zwingender Algorithmus auf hoher Ebene und es funktioniert für mich in der SQL-Einstellung ziemlich gut. Ich denke, dies wird als Top-Down-Ansatz angesehen, und Steven A. Lowe beschreibt einen ziemlich guten Bottom-Up- Ansatz.


1

Der Schlüssel zu Ihrer Frage liegt in dem, was Sie im vorletzten Absatz gesagt haben: "So kann man in SQL nicht reden." In dieser Phase kann es für Sie nützlicher sein, SQL als Fremdsprache anstelle einer Programmiersprache zu betrachten. Wenn Sie das so sehen, bedeutet das Schreiben einer SQL-Abfrage, dass Sie eine englische Aussage dessen, was Sie wollen, in "SQLish" übersetzen. Der Computer versteht SQLish perfekt und wird genau das tun, was Sie sagen, sodass Sie sich nicht um die Implementierung kümmern müssen, solange Sie korrekt übersetzen.

Was ist der beste Weg, um eine Fremdsprache zu lernen? Sie müssen natürlich die Grammatik und den Wortschatz lernen, die Sie aus Ihrer SQL-Dokumentation entnehmen können. Das Wichtigste ist die Übung. Sie sollten so viel SQL wie möglich lesen und schreiben und nicht das Gefühl haben, dass Sie die Syntax zuerst gründlich kennen müssen. Sie können und sollten nachsehen, wie Sie vorankommen. Sie werden wissen, dass Sie es haben, wenn Sie es einfacher finden, die gewünschten Daten in SQL als in Englisch zu beschreiben.


1

Ich habe lange gebraucht, um mich auch mit SQL zu beschäftigen. Wir haben an der Universität und zu der Zeit eine relationale Theorie gemacht, die nur dazu diente, die Dinge zu komplizieren. Am Ende war mein Lernprozess durch verschiedene Lernmaterialien und Beispiele, die ich auf dem Weg als nützlich empfand, sehr experimentell geprägt. Im Grunde werden Sie sich irgendwann daran gewöhnen, und eine neue Art des Denkens über Daten und Abfragen wird für Ihre mentale Entwicklung von Wert sein.

Ich stellte fest, dass ich mein Lernen beschleunigen konnte, indem ich nach und nach eine Sammlung einfacher Skripte aufbaute, in denen gezeigt wurde, wie die einzelnen Sprachfunktionen verwendet werden und wie bestimmte Ergebnisse in einer bekannten Tabelle erzielt werden (Tabellendefinitionen als Referenz enthalten).

Anfang dieses Jahres absolvierte ich ein formelles Training mit einem Datenmigrationsprojekt in einer unordentlichen Oracle-Datenbank, in dem ich nach und nach Fragmente aus meiner Bibliothek zusammensetzen musste, um die Abfrageergebnisse auf verschiedene Arten zu filtern, bis ich genau das hatte, was ich wollte, und sie dann umwandelte notwendig und so weiter. Einige der Abfragen wurden sehr komplex und schwer zu debuggen. Ich bezweifle, dass ich sie jetzt lesen konnte, aber ich hoffe, dass ich mit meinen Referenzbausteinen wieder zu einer ähnlichen Lösung kommen kann.

Andere Möglichkeiten, Ihr intuitives Bewusstsein für den deklarativen und funktionalen Raum zu stärken, sind das Erlernen von Mengenlehre und Programmiersprachen, die besser zu einem bestimmten Paradigma passen. Ich bin gerade dabei, etwas über Haskell zu lernen, um beispielsweise meine mathematischen Fähigkeiten zu erhalten und zu verbessern.


0

Wenn Sie auf ein Problem stoßen, überlegen Sie sich normalerweise, wie Sie es lösen können. Aber wenn Sie wissen, wie der Computer es für Sie löst! Dann sind Sie besorgt darüber, wie beseitigt wird.

Ich versuche zu sagen, wie es passiert.

Möglicherweise sind Sie bereits mit rekursiven Programmen vertraut. In rekursiven Programmen definieren Sie das Problem, statt zu sagen, wie es gelöst wird. Sie definieren die Basis und definieren n basierend auf n-1 . (zum Beispiel factorial(n) = n * factorial(n-1)) Möglicherweise wissen Sie bereits, wie der Computer das Problem löst. Es startet von der Funktion und ruft die Funktion rekursiv auf, bis es eine Basisdefinition erreicht. Anschließend werden alle anderen Funktionen basierend auf dem Basiswert ausgewertet.

Es ist das, was in der deklarativen Programmierung passiert. Sie definieren alles basierend auf den vorhandenen Definitionen. Und der Computer weiß, wie er anhand der Grundfunktionen die Antwort für Sie ableitet.

In SQL können Sie Definitionen nicht miteinander in Beziehung setzen, aber Sie verknüpfen Objekte oder Informationen miteinander, Sie geben an, was Sie möchten, und der Computer sucht auf der Grundlage der von Ihnen angegebenen Beziehungen nach etwas (Objekt, Informationen).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.