宣言型プログラミングと命令型プログラミング


24

命令型プログラミングに非常に満足しています。コンピューターに何をしてほしいかがわかったら、コンピューターに何をしたいのかをアルゴリズムで表現するのに問題はありません。しかし、SQLのような言語になると、頭が命令型プログラミングにあまりにも慣れているために行き詰まることがよくあります。

たとえば、band(bandName、bandCountry)、venue(venueName、venueCountry)、plays(bandName、venueName)のリレーションがあり、次のようなクエリを作成するとします。その名前の会場でプレーするその国。

例:すべての国(bandCountry)のバンドが演奏したすべての VenueNameが必要です。また、「関係」とは、SQLテーブルを意味します。

私はすぐに「venueNameごとに、すべてのbandCountriesを反復処理し、各bandCountryから、そこから派生するバンドのリストを取得します。いずれも、venueNameで再生しない場合は、次のVenueNameに移動します。繰り返しにより、venueNameを適切なVenueNamesのセットに追加します。

...しかし、SQLでそのように話すことはできません。実際、これをどのように定式化するかを考える必要があります。他の誰かがこの問題を抱えていましたか?これをどのように克服しましたか?パラダイムシフトを理解しましたか?命令型ソリューションを宣言型ソリューションに変換するために、命令型コンセプトからSQLコンセプトへのマップを作成しましたか?良い本を読む?

PS上記のクエリの解決策を探しているのではなく、解決しました。


1
これは良い質問です。なぜなら、あなたは自分自身を含む多くの人が持っている弱点を表明しているからです。
デビッドワイザー

質問の「関係」が何を意味するのかを定義すると役立つ場合があります。リレーショナルモデル(SQLの背後にある数学)では、「関係」はおおよそSQLテーブルに似ています。多くの人は、本当に「関係」と言うつもりなら「関係」と言います。
ジェイソンベイカー

集合論と離散数学を学びます。

1
@ Jase21、私は個人的に両方に精通していますが、SQLの重要なものはまだ面白いと感じています。クリーンな数学の例では、奇妙な現実世界のものを扱っていません。さらに、LINQを使用できるため、SQLに悩まされることはありません。最後に、質問者に:時間とともにそれに慣れるでしょう。
ジョブ

回答:


12

宣言的なことをやっての背後にある考え方は、あなたが指定することになっているということです何を、ではありません

私には、あなたは正しい軌道に乗っているように聞こえます。問題は、物事を間違った方法で考えているということではありません。行き過ぎだということです。あなたがやろうとしていることを見てみましょう:

たとえば、band(bandName、bandCountry)、venue(venueName、venueCountry)、plays(bandName、venueName)のリレーションがあり、次のようなクエリを作成するとします。その名前の会場でプレーするその国。

これまでのところ、これは素晴らしいことです。しかし、あなたはこれをします:

私はすぐに「venueNameごとに、すべてのbandCountriesを反復処理し、各bandCountryから、そこから派生するバンドのリストを取得します。いずれも、venueNameで再生しない場合は、次のVenueNameに移動します。繰り返しにより、venueNameを適切なVenueNamesのセットに追加します。

本質的に、あなたは不必要な仕事をしています。あなたはあなたが本当に欲しいものを知ってます。しかし、その後、あなたは それを取得し、それを取得する方法を見つけようとします。

もし私があなただったら、私は次の習慣を身につけようとします:

  1. 必要なものを定義ます。
  2. 取得方法の定義を意識的に止めてください。
  3. SQLで必要なものを表現する方法を見つけます。

ある程度の時間と労力を要するかもしれませんが、宣言型プログラミングを実際に理解すると、非常に便利になります。実際、コードの残りの部分で宣言型プログラミングを使用している場合があります。

本を探しているなら、SQL and Relational Theoryをお勧めします。SQLデータベースの背後にある理論を理解するのに本当に役立ちます。Dateの推奨事項を一粒の塩と一緒に摂取することを忘れないでください。彼は非常に良い情報を提供しますが、彼は時々少し意見を述べることができます。


どうやって何かを手に入れるかを考えることが間違ったアプローチであることがわかりません。使用している言語の種類は問題ではありません。あなたがやりたいことをする方法を理解する必要があります。
-davidk01

9

イテレータではなくセットの観点から考えてください。SQLステートメントは、目的の出力セット(別名テーブル/リレーション)のプロパティを定義します

すべてのVenueNamesのように、すべてのバンドに対して国はその国のバンドがその名前のVenueで演奏します

この結果(私があなたの意図を正しく理解していれば!)は、その会場で演奏する少なくとも1つのバンドを持つ会場のセットになります。PLAYSリレーションには探している情報が既に含まれているため、bandCountryの繰り返しは不要です。重複を排除するだけです。

したがって、SQLでは次のようになります。

select 
    distinct venueName
from PLAYS

編集:わかりました。したがって、実際に必要なセットはもう少し複雑です。データベースについて尋ねられる質問は、次のとおりです。すべての国からどの会場でバンドがホストされいますか?

そのため、目的のセットの要素のメンバーシップ基準を目標として定義し、逆方向に作業してセットに値を設定します。会場は、すべての国の少なくとも1つのバンドのPLAYS行がある場合、出力セットのメンバーです。この情報をどのように取得しますか?

1つの方法は、各会場の個別の国をカウントし、それをすべての国のカウントと比較することです。しかし、国との関係はありません。しばらく与えられたモデルについて考えると、すべての国のセットが正しい基準ではないことがわかります。少なくとも1つのバンドを持つすべての国のセットです。したがって、国テーブルは必要ありません(正規化されたモデルの場合は1つ必要です)。開催地の国は気にしません。たとえば、MS-SQL )

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

各会場のバンドの国をカウントできます

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

そして、サブクエリを使用して2つをつなぎ合わせることができます

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

今、それは可能な限りきれいなクエリではありません(GROUP BYとHAVINGは一時変数とサブクエリよりも「エレガントな」ソリューションと見なされるかもしれません) 。

OPの目的は、考え方を命令型から宣言型に変える方法を学ぶことでした。そのために、記述された命令型ソリューションが何をしていたかを見てください。

それぞれのVenueNameについて、すべてのbandCountriesを反復処理し、各bandCountryについて、そこから来るバンドのリストを取得します。いずれもvenueNameでプレイしない場合は、次のvenueNameに進みます。それ以外の場合、bandCountries反復の最後に、venueNameを適切なvenueNamesのセットに追加します

上記の決定基準は何ですか?私はそう思う:

...いずれも[特定の国のバンドのセット]がVenueNameで再生されない場合...

これは失格基準です。必須の思考プロセスは、完全なバケットから開始し、基準に適合しないものを捨てることです。私たちはしているフィルタリングデータを。

単純なものにはこれで問題ありませんが、目的の結果セットを構築する観点から考えると役立ちます。代わりにバケットを埋めることができる対応する資格基準は何ですか?

  • 失格者:会場で演奏するbandCountryのバンドがない場合、会場は失格となります
  • (部分)修飾子:bandCountryの少なくとも1つのバンドが会場で演奏する場合、会場は大丈夫かもしれません。バンドの残りの国をチェックし続ける
  • (full)qualifier:各バンドの国の少なくとも1つのバンドが会場で演奏する場合、会場は限定されます

最後の修飾子は、カウントを使用して簡略化できます。1つ以上のバンドが会場で再生される場合、bandCountryは「満足」です。会場の「満足」バンド国の数は、会場の資格を得るためのバンド国の数と等しくなければなりません。

これで、ナビゲーションによって関係全体を推論できます。

  • VENUEリレーションから開始します[答えには必要ありませんが、リレーショナルナビゲーションの概念的な出発点です]
  • VenueNameでPLAYSに参加する
  • bandNameでBANDに参加して、bandCountryを取得します
  • バンド名は気にしません。VenueNameとbandCountryのみを選択します
  • 冗長なbandCountriesは気にしません。DISTRICTまたはGROUP BYを使用して重複を削除する
  • 名前ではなく、個別のbandCountriesの数のみを考慮します
  • 個別のbandCountriesのカウントがbandCountriesの総数と同じ会場のみが必要です

上記の解決策(またはその合理的な複製)に戻る

概要

  • 集合論
  • リレーショナルナビゲーションパス
  • 包括的基準と排他的基準(適格か不適格か)

実際には、「すべての国(bandCountry> = venueCountry)のバンドが演奏した会場のセット」です。
EpsilonVector

@EpsilonVector:編集を参照
スティーブンA.ロウ

4

宣言型の考え方とプログラミングの方法を学習する1つの方法は、APLやJなどの汎用配列言語を学習することです。SQLは、宣言型のプログラミング方法を学習するための最良の手段ではないでしょう。APLまたはJでは、明示的なループまたは反復なしで、配列全体(ベクトル、行列、またはより高いランクの配列)を操作することを学びます。これにより、SQLおよび関係代数の理解がはるかに容易になります。非常に単純な例として、値が100より大きいベクトルVからアイテムを選択するために、APLで次のように記述します。

(V>100)/V

ここで、V> 100は、Vと同じ形状のブール配列に評価され、1が保持したい値をマークします。反復が行われていることは、ベテランのAPLerには発生しません。ベクトルVにマスクを適用し、新しいベクトルを返すだけです。もちろん、これは概念的にSQLのwhere句または関係代数制限操作が行うことです。

宣言型プログラミングは、多くのことをしなくても良いグリップを得ることができるとは思いません。また、SQLは一般的にあまりにも具体的です。ループやif / then / else構造、および命令型、手続き型、スカラー型のプログラミングに参加するすべての装置を使用せずに、多くの汎用コードを作成する必要があります。

この考え方にも役立つ他の関数型言語もありますが、配列言語はSQLに非常に似ています。


+1(「多くのことをせずに...良いグリップを得ることができない」)。また、命令型プログラミング(のような明らかに直観に反する構成要素a = a + 1)を一晩で習得した人もいませんでした。命令型プログラミングを学ぶのに時間がかかったように、論理や関数などの宣言型のスタイルを学ぶには時間がかかります。
ちょうど私の正しい意見

1

まず、両方を学ぶ必要があります。好みがあるかもしれませんが、他の方が優れている地域で作業するときは、戦わないでください。多くのプログラマーは、各レコードをステップスルーするのに非常に慣れているため、リレーショナルデータベースでカーソルを使用するように誘惑されますが、データベースはセットではるかに優れています。あなたは「私はこの方法でそれをする方法を知っており、私はほとんどコントロール、何とか、何とか、何とか」という考え方にはなりたくない。


1

あなたは命令的に考えることを学んだように宣言的に考えることを学びます:より単純な問題から始めて、「それを得る」ように取り組むことによって。

命令型プログラミングの最初の経験には、「a = a + 1」のような直感に反する(そして実際、まったくばかげた)一連のステートメントが含まれていました。あなたはそのことを心に留めて、今では文の明らかな不真実からの反動を思い出すことさえできないでしょう。宣言型スタイルの問題は、命令型スタイルを最初に始めたときの場所、つまり「無知な初心者」に戻っていることです。さらに悪いことに、この新しいスタイルとまったく対立するスタイルで何年も練習しており、「すべてのコストでコントロールする」という習慣のように、何年も元に戻す習慣があります。

宣言型スタイルは、今のところ直感に欠ける別のアプローチで機能します(長年にわたって数学のスキルを非常に鋭く保っていた場合を除き、ほとんどの人はそうしていません)。あなたは思考方法を再学習しなければならず、それを再学習する唯一の方法は一度に一つの簡単なステップを実行することです。

宣言型プログラミングへの最初の進出としてSQLを選択することは、概念を本当に学びたい場合は間違いかもしれません。それが基づいているタプル計算は確かに宣言的ですが、残念ながらタプル計算の純度は実装の現実によってひどく妥協されており、実際には言語は少し混乱した混乱になっています。あなたは次のように(ある意味であなたが使用している)他のより直接的に有用で宣言型言語を見て代わりたいことのLisp(特にスキーム)、ハスケルのML(主に)関数型プログラミングのための、あるいは、プロローグ水星のための(主に)論理プログラミング。

私の意見では、これらの他の言語を学習することで、いくつかの理由で宣言型プログラミングがどのように機能するかについてより良い洞察が得られます。

  1. これらは、「ゆりかごから墓場まで」のプログラミングに役立ちます。これらの言語で最初から最後まで完全なプログラムを書くことができます。スタンドアロン言語としてほとんどの人にとって本当に役に立たないSQLとは異なり、これらはスタンドアロンで有用です。

  2. これらはそれぞれ、最終的に「取得」するためのさまざまな道を提供できる宣言型プログラミングについて、異なる傾斜を提供します。

  3. また、それぞれプログラミング全般について考える上で、それぞれ異なる傾向があります。直接使用しない場合でも、問題とコーディングについて推論する能力が向上します。

  4. それらから学ぶ教訓は、SQLでも役立ちます。特に、データについて純粋に考えるためにリレーショナルデータベースの背後にあるタプル計算を磨く場合はなおさらです。

関数型言語(Lispの1つであるClojureがおそらくここでの選択である)論理言語(私はMercuryが一番好きですが、Prologには学習に役立つ資料がたくさんあります)の1つを学ぶことを特にお勧めします思考プロセスを最大限に拡張します。


1

SQLのような宣言的な設定で命令的に考えることは間違っていません。それは、命令的な思考が、あなたが説明したよりも少し高いレベルで起こっているということです。SQLを使用するデータベースを照会する必要があるときはいつでも、私は常に自分自身に考えます。

  • 必要なものは次のとおりです。
  • この方法でそれらをまとめるつもりです。
  • 私が本当に探しているものに到達するために、次の述語で得たものを削ります。

上記は高レベルの命令型アルゴリズムであり、SQL設定では非常にうまく機能します。これはトップダウンアプローチと考えられていると思います。StevenA. Loweは、かなり良いボトムアップアプローチについて説明ています。


1

あなたの質問の鍵は、あなたが最後から2番目の段落で言ったことにあります:「あなたはSQLでそのように話すことはできません。」この段階で、プログラミング言語ではなく外国語としてSQLにアプローチする方が便利な場合があります。このように考えると、SQLクエリを記述することは、実際にあなたが望むものの英語のステートメントを「SQLish」に翻訳することです。コンピューターはSQLishを完全に理解し、あなたが言うことを正確に実行します。したがって、正しく翻訳する限り、実装について心配する必要はありません。

そうは言っても、外国語を学ぶ最良の方法は何ですか?明らかに、文法と語彙を学ぶ必要があります。これらはSQLのドキュメントから入手できます。最も重要なことは練習です。できるだけ多くのSQLを読み書きする必要があります。最初に構文を完全に知る必要があるとは思わないでください。進むにつれて物事を調べることができますし、そうすべきです。必要なデータを英語で記述するよりもSQLで記述する方が簡単であることがわかったとき、それを知っているでしょう。


1

SQLに頭を包むのにも時間がかかりました。私たちは大学で、そして当時、問題を複雑にするだけの役にたついくつかの関係理論を行いました。結局、私の学習プロセスは、試行錯誤であり、さまざまな学習教材と例から得られたものであり、その過程で役に立つことがわかりました。基本的に、最終的にはそれに慣れるでしょうし、データやクエリについての新しい考え方を追加することは、あなたの精神発達にとっていくらかの価値があります。

各言語機能の使用方法と既知のテーブルで特定の結果を得る方法を示す簡単なスクリプトのコレクションを徐々に構築することで、学習を加速できることがわかりました(参照用にテーブル定義が含まれています)。

今年の初めに、乱雑なOracleデータベースでのデータ移行プロジェクトに関連した正式なトレーニングを行いました。必要など。一部のクエリは非常に複雑になり、デバッグが困難になりました。私は今それらを読むことができるとは思わないが、参照ビルディングブロックを使用することで同様の解決策に再び到達できることを望んでいる。

宣言的および機能的空間に対する直感的な認識を高める他の方法は、特定のパラダイムにより適した学習セット理論およびプログラミング言語です。私は現在、例えば数学の能力を維持および改善するために、Haskellをいくつか学習している最中です。


0

問題に直面するとき、あなたは通常それを解決する方法を考えます。しかし、コンピューターがあなたのためにそれをどのように解決するかを知っているなら!次に、どのように除去されるが心配です。

私はそれがどのように起こるかを言おうとしています。

あなたはすでに再帰プログラムに精通しているかもしれません。再帰プログラムでは、問題を解決する方法を言うのではなく、問題を定義します。あなたは定義ベースを、と定義するn個に基づいてN-1 。(例factorial(n) = n * factorial(n-1))しかし、あなたはすでにコンピュータがそれをどのように解決する知っているかもしれません。関数から開始し、基本定義に到達するまで関数を再帰的に呼び出してから、基本値に基づいて他のすべての関数を評価します。

宣言型プログラミングで起こることです。既存の定義に基づいてすべてを定義します。そして、コンピューターは基本的な機能に基づいて答えを導き出す方法を知っています。

SQLでは、定義を相互に関連付けることはできませんが、オブジェクトまたは情報を相互に関連付け、提供する関係に基づいて、必要なものを指定し、コンピューター検索を何か(オブジェクト、情報)に関連付けます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.