Programmation déclarative vs programmation impérative

24

Je me sens très à l'aise avec la programmation impérative. Je n'ai jamais de difficulté à exprimer de façon algorithmique ce que je veux que l'ordinateur fasse une fois que j'ai compris ce que je veux qu'il fasse. Mais quand il s'agit de langages comme SQL ou je suis souvent bloqué parce que ma tête est trop habituée à la programmation impérative.

Par exemple, supposons que vous ayez les relations band (bandName, bandCountry), venue (venueName, venueCountry), pièces de théâtre (bandName, venueName), et je veux écrire une requête qui dit: tous les venueNames tels que pour chaque bandCountry il y a un groupe de ce pays qui joue dans le lieu de ce nom.

Exemple: Je veux tous les noms de lieu dans lesquels des groupes de tous les pays (bandCountry) ont joué. Aussi, par "relation", j'entends une table SQL.

Dans mon esprit, je vais immédiatement "pour chaque lieuName itérer sur tous les groupes de pays et pour chaque groupe de pays obtenir la liste des groupes qui en découlent. l'itération ajoute venueName à l'ensemble des bons venueNames ".

... mais vous ne pouvez pas parler comme ça en SQL et j'ai vraiment besoin de réfléchir à la façon de formuler cela, avec la solution intuitive Imperative qui me harcèle constamment à l'arrière de ma tête. Quelqu'un d'autre a-t-il eu ce problème? Comment avez-vous surmonté cela? Avez-vous trouvé un changement de paradigme? Vous avez fait une carte des concepts impératifs aux concepts SQL pour traduire les solutions impératives en solutions déclaratives? Lire un bon livre?

PS Je ne cherche pas de solution à la requête ci-dessus, je l'ai résolue.

EpsilonVector
la source
1
C'est une bonne question parce que vous exprimez une faiblesse que beaucoup (y compris moi-même) ont.
David Weiser
Il pourrait être utile de définir ce que vous entendez par «relation» dans votre question. Dans le modèle relationnel (les mathématiques derrière SQL), la "relation" est à peu près analogue à une table SQL. Beaucoup de gens diront «relation» quand ils veulent vraiment dire «relation».
Jason Baker
Apprenez la théorie des ensembles et les mathématiques discrètes.
1
@ Jase21, personnellement, je connais assez bien les deux, mais les choses non triviales en SQL me semblent toujours drôles. Aucun des exemples mathématiques propres ne traite des trucs étranges du monde réel. De plus, on peut utiliser LINQ, et donc ne pas être dérangé par SQL. Enfin, au demandeur: vous vous y habituerez avec le temps.
Job

Réponses:

12

L'idée derrière faire les choses de manière déclarative est que vous êtes censé spécifier quoi , pas comment .

Pour moi, il semble que vous soyez sur la bonne voie. Le problème n'est pas que vous pensez mal aux choses. C'est que tu vas trop loin. Voyons ce que vous essayez de faire:

Par exemple, supposons que vous ayez les relations band (bandName, bandCountry), venue (venueName, venueCountry), pièces de théâtre (bandName, venueName), et je veux écrire une requête qui dit: tous les venueNames tels que pour chaque bandCountry il y a un groupe de ce pays qui joue dans le lieu de ce nom.

Jusqu'à présent, c'est super. Mais alors vous faites ceci:

Dans mon esprit, je vais immédiatement "pour chaque lieuName itérer sur tous les groupes de pays et pour chaque groupe de pays obtenir la liste des groupes qui en découlent. l'itération ajoute venueName à l'ensemble des bons venueNames ".

Essentiellement, vous faites un travail inutile. Vous savez ce que vous voulez, c'est tout ce dont vous avez vraiment besoin. Mais ensuite vous continuez et essayez de comprendre comment l' obtenir.

Si j'étais vous, j'essaierais de prendre l'habitude suivante:

  1. Définissez ce que vous voulez.
  2. Vous empêchez consciemment de définir comment l' obtenir.
  3. Découvrez comment représenter ce que vous voulez en SQL.

Cela peut prendre du temps et des efforts de votre part, mais une fois que vous avez vraiment fait de la programmation déclarative, cela devient très utile. En fait, vous pourriez vous retrouver à utiliser la programmation déclarative dans le reste de votre code.

Si vous cherchez un livre, je recommanderais SQL et la théorie relationnelle . Cela vous aide vraiment à comprendre la théorie derrière les bases de données SQL. N'oubliez pas de prendre les recommandations de Date avec un grain de sel. Il donne de très bonnes informations, mais il peut parfois être un peu opiniâtre.

Jason Baker
la source
Je ne comprends pas comment trouver comment obtenir quelque chose est une mauvaise approche. Peu importe le type de langage que vous utilisez, vous devez savoir comment lui dire de faire ce que vous voulez.
davidk01
9

penser en termes d'ensembles, pas d'itérateurs; les instructions sql définissent les propriétés de l'ensemble de sortie souhaité (aka table / relation)

tous les lieuxNoms tels que pour chaque groupePays il y a un groupe de ce pays qui joue dans le lieu de ce nom

le résultat de cela (si j'ai bien compris vos intentions!) serait l'ensemble des salles qui ont au moins un groupe qui joue dans cette salle. L'itération sur bandCountry n'est pas nécessaire, car la relation PLAYS a déjà les informations que vous recherchez, il vous suffit d'éliminer les doublons

donc en SQL ce serait:

select 
    distinct venueName
from PLAYS

EDIT: ok, donc l'ensemble réel souhaité est un peu plus compliqué. La question posée à la base de données est: quels lieux ont accueilli des groupes de tous les pays?

Ainsi, nous définissons les critères d'appartenance à un élément de l'ensemble souhaité comme objectif, puis travaillons à l'envers pour remplir l'ensemble. Un lieu est membre de l'ensemble de sortie s'il a une ligne PLAYS pour au moins un groupe de chaque pays. Comment obtenons-nous ces informations?

Une façon consiste à compter les pays distincts pour chaque site et à les comparer au nombre de tous les pays. Mais nous n'avons pas de relation PAYS. Si nous réfléchissons un instant au modèle donné, nous voyons que l'ensemble de tous les pays n'est pas le bon critère; c'est l'ensemble de tous les pays qui ont au moins une bande. Nous n'avons donc pas besoin d'une table de pays (bien que pour un modèle normalisé nous devrions en avoir une), et nous ne nous soucions pas du pays du lieu, nous pouvons simplement compter les pays qui ont des bandes, par exemple (dans MS-SQL )

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

Nous pouvons compter les pays du groupe pour chaque lieu

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

et nous pouvons assembler les deux en utilisant une sous-requête

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

Maintenant, ce n'est pas la plus jolie requête possible (GROUP BY et HAVING pourraient être considérés comme une solution plus `` élégante '' que les variables temporaires et une sous-requête), mais il est assez évident de savoir ce que nous recherchons, nous allons donc en rester là pour le but de l'OP .

Le but du PO était d'apprendre à faire passer l'état d'esprit de l'impératif au déclaratif. À cette fin, regardez ce que la solution impérative décrite faisait:

pour chaque lieu, nommez tous les groupes de pays et pour chaque groupe de pays, obtenez la liste des groupes qui en proviennent. Si aucun d'entre eux ne joue dans lieuName, passez au lieu suivant. Sinon, à la fin de l'itération bandCountries, ajoutez lieuName à l'ensemble des bons lieuxNoms

Quels sont les critères déterminants ci-dessus? Je pense que c'est:

... Si aucun d'entre eux [l'ensemble des groupes d'un pays particulier] ne joue dans venueName ...

Il s'agit d'un critère de disqualification . Le processus de pensée impératif commence par un seau plein et jette des choses qui ne correspondent pas aux critères. Nous filtrons les données.

C'est bien pour des choses simples, mais cela aide à penser en termes de construction de l'ensemble de résultats souhaité; quels sont les critères de qualification correspondants qui permettraient de remplir le seau à la place?

  • disqualifier: s'il n'y a pas de groupe d'un groupe de pays qui joue dans un lieu, le lieu est disqualifié
  • qualificatif (partiel): si au moins un groupe d'un groupe de pays joue sur un lieu, le lieu peut être correct; continuez à vérifier le reste du groupe
  • qualificatif (complet): si au moins un groupe de chaque groupe joue dans un lieu, le lieu est qualifié

Le qualificatif final peut être simplifié en utilisant les décomptes: un groupe de pays est «satisfait» si au moins un groupe de là joue sur un site; le nombre de pays de bande «satisfaits» pour un lieu doit être égal au nombre de pays de bande pour le lieu à qualifier.

Maintenant, nous pouvons raisonner à travers les relations par navigation:

  • commencer par la relation VENUE [nous n'en avons pas besoin pour la réponse, mais c'est le point de départ conceptuel de la navigation relationnelle]
  • rejoindre PLAYS sur venueName
  • rejoindre BAND sur bandName pour obtenir le groupe
  • nous ne nous soucions pas du nom du groupe; sélectionner uniquement le lieuName et le groupeCountry
  • nous ne nous soucions pas des pays de bande redondants; éliminer les doublons à l'aide de DISTRICT ou GROUP BY
  • nous ne nous soucions que du nombre de pays distincts, pas des noms
  • nous ne voulons que des lieux où le nombre de pays de bande distincts est le même que le nombre total de pays de bande

ce qui nous ramène à la solution ci-dessus (ou à un fac-similé raisonnable de celle-ci)

SOMMAIRE

  • théorie des ensembles
  • chemins de navigation relationnelle
  • critères inclusifs vs exclusifs (qualification vs disqualification)
Steven A. Lowe
la source
C'est en fait "un ensemble de lieux où des groupes de tous les pays (bandCountry> = venueCountry) y ont joué".
EpsilonVector
@EpsilonVector: voir les modifications
Steven A. Lowe
4

Une façon d'apprendre à penser et à programmer dans un style déclaratif est d'apprendre un langage de tableau à usage général comme APL ou J. SQL n'est probablement pas le meilleur véhicule pour apprendre à programmer de manière déclarative. En APL ou J, vous apprenez à opérer sur des tableaux entiers (vecteurs, matrices ou tableaux de rang supérieur), sans boucle ni itération explicites. Cela facilite la compréhension de SQL et de l'algèbre relationnelle. A titre d'exemple très simple, pour sélectionner des éléments d'un vecteur V dont la valeur est supérieure à 100, en APL on écrit:

(V>100)/V

Ici, V> 100 correspond à un tableau booléen de la même forme que V, avec 1 marquant les valeurs que nous voulons conserver. Il n'apparaît pas à l'APLer chevronné qu'il y a une itération en cours, nous appliquons simplement un masque au vecteur V, renvoyant un nouveau vecteur. C'est bien sûr conceptuellement ce que fait une opération SQL clause ou algèbre relationnelle restreindre.

Je ne pense pas que vous puissiez avoir une bonne maîtrise de la programmation déclarative sans en faire beaucoup, et SQL est généralement trop spécifique. Vous devez écrire beaucoup de code à usage général, apprendre à vous passer des boucles et des structures if / then / else et de tout l'appareil qui assiste à la programmation impérative, procédurale et scalaire.

Il peut y avoir d'autres langages fonctionnels qui aident à cette façon de penser aussi, mais les langages de tableaux sont très proches de SQL.

PAUL Mansour
la source
+1 pour "[vous ne pouvez pas] obtenir une bonne adhérence ... sans en faire beaucoup". Personne n'a appris la programmation impérative (avec ses constructions clairement contre-intuitives comme a = a + 1) du jour au lendemain. Il faut du temps pour apprendre des styles déclaratifs comme la logique, le fonctionnel, etc., tout comme il a fallu du temps pour apprendre la programmation impérative.
JUSTE MON AVIS correct
1

Tout d'abord, vous devez apprendre les deux. Vous pouvez avoir une préférence, mais lorsque vous travaillez dans des domaines où l'autre est meilleur, ne le combattez pas. Beaucoup de programmeurs sont tentés d'utiliser des curseurs dans les bases de données relationnelles car ils sont si habitués à parcourir chaque enregistrement, mais la base de données est bien meilleure dans les ensembles. Vous ne voulez pas entrer dans l'état d'esprit "Je sais comment faire de cette façon et j'ai le plus de contrôle, bla, bla, bla".

JeffO
la source
1

Vous apprenez à penser de manière déclarative comme vous avez appris à penser impérativement: en vous entraînant en commençant par des problèmes plus simples et en progressant au fur et à mesure que vous le comprenez.

Vos premières expériences avec la programmation impérative ont inclus tout un tas de déclarations contre-intuitives (et, en fait, tout à fait ridicules) comme " a = a + 1". Vous avez tourné votre esprit autour de cela au point que vous ne vous souvenez probablement plus du recul de la contre-vérité évidente de la déclaration. Votre problème avec les styles déclaratifs est que vous êtes de retour là où vous étiez lorsque vous avez commencé avec les styles impératifs: un "newb clueless". Pire encore, vous avez des années de pratique avec un style qui est totalement en contradiction avec ce nouveau style et vous avez des années d'habitudes à défaire - comme l'habitude de "contrôler à tout prix".

Les styles déclaratifs fonctionnent avec une approche différente qui vous manque d'intuition pour l'instant (sauf si vous avez gardé vos compétences en mathématiques très pointues au fil des ans - ce que la plupart des gens ne font pas). Vous devez réapprendre à penser et la seule façon de réapprendre est de le faire, une étape simple à la fois.

Choisir SQL comme première incursion dans la programmation déclarative peut être une erreur si vous voulez vraiment apprendre les concepts. Bien sûr, le calcul de tuple sur lequel il est basé est aussi déclaratif que possible, mais malheureusement, la pureté du calcul de tuple a été gravement compromise par les réalités de la mise en œuvre et le langage est, en fait, devenu un peu un désordre confus. Vous voudrez peut-être plutôt regarder d'autres langages déclaratifs plus directement utiles (dans le sens où vous en avez l'habitude) comme les Lisps (en particulier le schéma ), Haskell et les ML pour la programmation fonctionnelle (principalement) ou, alternativement, Prolog et Mercury pour (principalement) programmation logique.

L'apprentissage de ces autres langues vous donnera un meilleur aperçu, à mon avis, du fonctionnement de la programmation déclarative pour plusieurs raisons:

  1. Ils sont utiles pour la programmation "du berceau à la tombe" - comme dans vous pouvez écrire un programme complet dans ces langues du début à la fin. Ils sont utiles seuls, contrairement à SQL qui est vraiment très inutile pour la plupart des gens en tant que langage autonome.

  2. Ils vous donnent chacun une perspective différente sur la programmation déclarative qui peut vous donner différentes routes pour enfin «l'obtenir».

  3. Ils vous donnent également chacun une vision différente de la programmation en général. Ils amélioreront votre capacité à raisonner sur les problèmes et le codage même si vous ne les utilisez jamais directement vous-même.

  4. Les leçons que vous en tirerez vous aideront également avec votre SQL - en particulier si vous rafraîchissez le calcul des tuples derrière les bases de données relationnelles pour la forme pure de réflexion sur les données.

Je recommanderais surtout d'apprendre l'un des langages fonctionnels ( Clojure , comme l'un des Lisps, est probablement un bon choix ici) et l' un des langages logiques (j'aime mieux Mercury, mais Prolog a beaucoup plus de matériel utile pour l'apprentissage) pour une expansion maximale du processus de pensée.

JUSTE MON AVIS correct
la source
1

Il n'est pas faux de penser impérativement dans un cadre déclaratif comme SQL. C'est juste que la pensée impérative devrait se produire à un niveau un peu plus élevé que ce que vous avez décrit. Chaque fois que j'ai besoin d'interroger une base de données qui utilise SQL, je pense toujours à moi-même:

  • Voici les pièces dont j'ai besoin.
  • Je vais les assembler de cette façon.
  • Je vais réduire ce que je viens d'obtenir avec les prédicats suivants pour arriver à ce que je recherche vraiment.

Ce qui précède est un algorithme impératif de haut niveau et il fonctionne assez bien pour moi dans le paramètre SQL. Je pense que cela est considéré comme une approche descendante et Steven A. Lowe décrire une assez bonne bottom-up approche.

davidk01
la source
1

La clé de votre question est dans ce que vous avez dit dans l'avant-dernier paragraphe: "Vous ne pouvez pas parler comme ça en SQL." Il peut être plus utile pour vous à ce stade d'aborder SQL comme une langue étrangère au lieu d'un langage de programmation. Si vous y pensez de cette façon, écrire une requête SQL est vraiment traduire une déclaration en anglais de ce que vous voulez en "SQLish". L'ordinateur comprend parfaitement SQLish et fera exactement ce que vous dites, vous n'avez donc pas à vous soucier de l'implémentation tant que vous traduisez correctement.

Cela dit, quelle est la meilleure façon d'apprendre une langue étrangère? Vous devez évidemment apprendre la grammaire et le vocabulaire, que vous pouvez obtenir dans votre documentation SQL. La chose la plus importante est la pratique. Vous devez lire et écrire autant de SQL que possible, et ne pensez pas que vous devez d'abord connaître la syntaxe à fond; vous pouvez et devez rechercher les choses au fur et à mesure. Vous saurez que vous l'avez quand vous trouverez plus facile de décrire quelles données vous voulez en SQL qu'en anglais.

Larry Coleman
la source
1

Cela m'a également pris beaucoup de temps pour comprendre SQL. Nous avons fait une théorie relationnelle à l'université et à l'époque, qui ne faisait que compliquer les choses. En fin de compte, mon processus d'apprentissage a été beaucoup d'essais et d'erreurs éclairés par divers supports d'apprentissage et exemples que j'ai trouvés utiles en cours de route. Essentiellement, vous vous y habituerez éventuellement, et l'ajout d'une nouvelle façon de penser les données et les requêtes sera d'une certaine valeur pour votre développement mental.

J'ai trouvé que j'étais en mesure d'accélérer mon apprentissage en créant progressivement une collection de scripts simples montrant comment utiliser chaque fonctionnalité de langue et comment obtenir certains résultats sur une table connue (définitions de table incluses pour référence).

Plus tôt cette année, j'ai suivi une formation formelle impliquant un projet de migration de données sur une base de données Oracle désordonnée, où j'ai dû progressivement reconstituer des fragments de ma bibliothèque pour filtrer les résultats de la requête de différentes manières jusqu'à ce que j'aie exactement ce que je voulais, puis les transformer en nécessaire, etc. Certaines requêtes sont devenues très complexes et difficiles à déboguer. Je doute que je puisse les lire maintenant, mais j'espère que je pourrais arriver à une solution similaire à nouveau en utilisant mes blocs de construction de référence.

D'autres façons d'augmenter votre conscience intuitive des espaces déclaratifs et fonctionnels sont l'apprentissage de la théorie des ensembles et des langages de programmation plus adaptés à un paradigme particulier. Je suis actuellement en train d'apprendre du Haskell, par exemple, pour maintenir et améliorer mes capacités mathématiques.

IanGilham
la source
0

Lorsque vous rencontrez un problème, vous pensez généralement comment le résoudre. Mais si vous savez comment l'ordinateur le résout pour vous! Vous vous demandez alors comment les éliminer.

J'essaie de dire comment ça se passe.

Vous connaissez peut-être déjà les programmes récursifs, dans les programmes récursifs, vous définissez le problème plutôt que de dire comment il est résolu. vous définissez la base et définissez n sur la base de n-1 . (par exemple factorial(n) = n * factorial(n-1)) Mais vous savez peut-être déjà comment l'ordinateur le résout. il part de la fonction et appelle la fonction récursivement jusqu'à ce qu'il atteigne une définition de base, puis évalue toutes les autres fonctions en fonction de la valeur de base.

C'est ce qui se passe dans la programmation déclarative. vous définissez tout sur la base des définitions existantes. Et l'ordinateur sait comment obtenir la réponse pour vous en fonction des fonctions de base.

En SQL, vous ne pouvez pas relier les définitions les unes aux autres, mais vous reliez les objets ou les informations les uns aux autres, vous spécifiez ce que vous voulez et la recherche informatique à quelque chose (objet, informations) en fonction des relations que vous avez fournies.

Ahmad
la source