私はWebを検索して、私に光を当てる宣言型および命令型プログラミングの定義を探しています。しかし、私が見つけたいくつかのリソースで使用されている言語は、たとえばWikipediaなどでは気が遠くなるようなものです。この主題に何らかの視点をもたらす可能性のある実際の例を誰かが私に示すことができますか(おそらくC#で)?
19 に答える
宣言型プログラミングと命令型プログラミングの優れたC#の例は、LINQです。
命令型プログラミングでは、何をしたいのかを段階的にコンパイラーに伝えます。
たとえば、このコレクションから始めて、奇数を選択しましょう。
List<int> collection = new List<int> { 1, 2, 3, 4, 5 };
命令型プログラミングでは、これを段階的に実行し、必要なものを決定します。
List<int> results = new List<int>();
foreach(var num in collection)
{
if (num % 2 != 0)
results.Add(num);
}
ここで、私たちは言っています:
- 結果コレクションを作成する
- コレクション内の各番号をステップスルーします
- 数値を確認し、奇数の場合は結果に追加します
一方、宣言型プログラミングでは、必要なものを説明するコードを記述しますが、必ずしもそれを取得する方法を説明する必要はありません(目的の結果を宣言しますが、ステップバイステップではありません)。
var results = collection.Where( num => num % 2 != 0);
ここでは、「コレクションをステップスルーします。このアイテムをチェックし、奇妙な場合は結果コレクションに追加します」ではなく、「奇妙な場所にすべてを提供してください」と言っています。
多くの場合、コードも両方のデザインが混在しているため、必ずしも白黒であるとは限りません。
宣言型プログラミングはあなたが望むものを言うときであり、命令型言語はあなたがあなたが望むものを手に入れる方法を言うときです。
Pythonの簡単な例:
# Declarative
small_nums = [x for x in range(20) if x < 5]
# Imperative
small_nums = []
for i in range(20):
if i < 5:
small_nums.append(i)
最初の例は、リストを作成するための「実装の詳細」を指定していないため、宣言型です。
C#の例に結び付けるには、一般に、LINQを使用すると、必要なものを取得する方法を言っていないため、宣言型のスタイルになります。あなたはあなたが望むことを言っているだけです。SQLについても同じことが言えます。
宣言型プログラミングの利点の1つは、コンパイラーが、手動で行うよりも優れたコードをもたらす可能性のある決定を行えることです。次のようなクエリがある場合は、SQLの例を使用して実行します
SELECT score FROM games WHERE id < 100;
SQLの「コンパイラ」は、インデックス付きのフィールドであることがわかっているため、このクエリを「最適化」できます。id
または、インデックスが作成されていない場合は、データセット全体を反復処理する必要があります。あるいは、SQLエンジンは、これが8つのコアすべてを利用して高速な並列検索を行うのに最適な時期であることを知っているかもしれません。 あなたはプログラマーとして、これらの条件のいずれにも関心がなく、そのような特別な場合を処理するためにコードを記述する必要はありません。
宣言型と命令型
プログラミングパラダイムは、コンピュータープログラミングの基本的なスタイルです。4つの主要なパラダイムがあります。命令型、宣言型、機能型(宣言型パラダイムのサブセットと見なされます)、およびオブジェクト指向です。
宣言型プログラミング:制御フロー(How do)を記述せずに、計算のロジック(What do)を表現するプログラミングパラダイムです。宣言型ドメイン固有言語(DSL)のよく知られた例には、CSS、正規表現、SQLのサブセット(SELECTクエリなど)が含まれます。HTML、MXML、XAML、XSLT...などの多くのマークアップ言語は宣言型であることがよくあります。宣言型プログラミングは、一連の命令としてのプログラムと、目的の答えについてのアサーションとしてのプログラムとの区別を曖昧にしようとします。
命令型プログラミング:プログラムの状態を変更するステートメントの観点から計算を記述するプログラミングパラダイムです。命令型プログラムは、プログラミングコマンドまたは数学的アサーションとして二重に見ることができます。
関数型プログラミング:計算を数学関数の評価として扱い、状態および可変データを回避するプログラミングパラダイムです。状態の変化を強調する命令型プログラミングスタイルとは対照的に、関数の適用を強調します。Haskellなどの純粋な関数型言語では、すべての関数に副作用がなく、状態の変化は状態を変換する関数としてのみ表されます。
次のMSDNの命令型プログラミングの例では、1から10までの数字をループして、偶数を見つけます。
var numbersOneThroughTen = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//With imperative programming, we'd step through this, and decide what we want:
var evenNumbers = new List<int>();
foreach (var number in numbersOneThroughTen)
{ if (number % 2 == 0)
{
evenNumbers.Add(number);
}
}
//The following code uses declarative programming to accomplish the same thing.
// Here, we're saying "Give us everything where it's even"
var evenNumbers = numbersOneThroughTen.Where(number => number % 2 == 0);
どちらの例でも同じ結果が得られ、一方が他方より良くも悪くもありません。最初の例ではより多くのコードが必要ですが、コードはテスト可能であり、命令型のアプローチにより、実装の詳細を完全に制御できます。2番目の例では、コードは間違いなく読みやすくなっています。ただし、LINQでは、舞台裏で何が起こるかを制御することはできません。LINQが要求された結果を提供することを信頼する必要があります。
ここおよび他のオンライン投稿での回答は次のとおりです。
- 宣言型プログラミングでは、必要なものを記述したコードを記述しますが、必ずしもそれを取得する方法とは限りません。
- 命令型プログラミングよりも宣言型プログラミングを優先する必要があります
彼らが私たちに言っていないのは、それを達成する方法です。プログラムの一部をより宣言的にするには、他の部分が抽象化を提供して、実装の詳細(命令型コード)を非表示にする必要があります。
- たとえば、LINQはループ(for、whileなど)よりも宣言型です。たとえば、
list.Where()
新しいフィルタリングされたリストを取得するために使用できます。これが機能するために、MicrosoftはLINQ抽象化の背後にあるすべての面倒な作業を行いました。
実際、関数型プログラミングと関数型ライブラリがより宣言的である理由の1つは、ループとリストの作成を抽象化し、すべての実装の詳細(おそらくループ付きの命令型コード)をバックグラウンドで隠しているためです。
どのプログラムでも、常に命令型コードと宣言型コードの両方があり、プログラムの他の部分が宣言型で使用できるように、ドメイン固有の抽象化の背後にすべての命令型コードを隠すことを目指す必要があります。
最後に、関数型プログラミングとLINQを使用すると、プログラムをより宣言型にすることができますが、より多くの抽象化を提供することで、いつでもプログラムをさらに宣言型にすることができます。例えば:
// JavaScript example
// Least declarative
const bestProducts = [];
for(let i = 0; i < products.length; i++) {
let product = products[i];
if (product.rating >= 5 && product.price < 100) {
bestProducts.push(product);
}
}
// More declarative
const bestProducts = products.filter(function(product) {
return product.rating >= 5 && product.price < 100;
});
// Most declarative, implementation details are hidden in a function
const bestProducts = getBestProducts();
PS宣言型プログラミングの極限は、新しいドメイン固有言語(DSL)を発明することです。
- 文字列検索:カスタム命令型コードの代わりに正規表現
- React.js:DOMを直接操作する代わりにJSX
- AWS CloudFormation:CLIではなくYAML
- リレーショナルデータベース:ISAMやVSAMなどの古い読み取り/書き込みAPIの代わりにSQL。
宣言型/命令型プログラミングの議論ではめったにポップアップしない別の例を追加します:ユーザーインターフェイス!
C#では、さまざまなテクノロジーを使用してUIを構築できます。
命令型では、DirectXまたはOpenGLを使用して、ボタンやチェックボックスなどを1行ずつ(または実際には三角形ごとに)非常に命令的に描画できます。ユーザーインターフェイスの描画方法を言うのはあなた次第です。
宣言型の最後に、WPFがあります。基本的にXML(技術的には「XAML」)を記述し、フレームワークが自動的に機能します。ユーザーインターフェイスがどのように見えるかを言います。それを行う方法を理解するのはシステム次第です。
とにかく、考えるべきもう一つのこと。一方の言語が宣言型または命令型であるからといって、それが他方の特定の機能を備えていないことを意味するわけではありません。
また、宣言型プログラミングの利点の1つは、通常、コードを読み取ることで目的をより簡単に理解できるのに対し、命令型プログラミングでは実行をより細かく制御できることです。
そのすべての要点:
宣言型->what
やりたい
命令型->how
あなたはそれをやりたい
ケンブリッジのコースの説明とその例が気に入りました。
- 宣言型-実行方法ではなく、実行することを指定します
- 例:HTMLは、画面にどのように描画するかではなく、Webページに表示する内容を記述します
- 命令型-何をどのように指定するか
int x;
-何(宣言型)x=x+1;
- どうやって
違いは主に、抽象化の全体的なレベルに関係しています。宣言型では、ある時点で、個々のステップから遠く離れているため、プログラムは結果を取得する方法に関して多くの自由度を持っています。
すべての指示は、連続体のどこかにあると見なすことができます。
抽象化の程度:
Declarative <<=====|==================>> Imperative
宣言型の実世界の例:
- 司書、MobyDickのコピーをチェックしてください。(司書は、彼らの裁量で、要求を実行するための最良の方法を選択します)
命令型の実例:
- 図書館に行く
- 本の整理システムを探す(カード目録-オールドスクール)
- カード目録の使い方を調べましょう(あなたも忘れてしまいましたね)
- 棚にラベルが付けられ、整理されている方法を理解します。
- 本が棚にどのように整理されているかを理解します。
- カード目録から組織システムと本の場所を相互参照して、その本を見つけます。
- チェックアウトシステムに本を持っていきます。
- 本をチェックしてください。
命令型プログラミングでは、開発者がコードの実行方法を段階的に定義する必要があります。命令型の方法で道順を示すには、「1st Streetに行き、左折してMainに入り、2ブロック進み、右折してMapleに入り、左側の3番目の家に立ち寄ります」と言います。宣言型バージョンは、「スーの家に車で行く」のように聞こえるかもしれません。何かをする方法を言う人がいます。もう1つは、何をする必要があるかを示しています。
宣言型のスタイルには、命令型のスタイルに比べて2つの利点があります。
- それは旅行者に長い一連の指示を暗記することを強制しません。
- これにより、旅行者は可能な場合にルートを最適化できます。
Calvert、C Kulkarni、D(2009)。エッセンシャルLINQ。アディソンウェスリー。48。
命令型プログラミングとは、順序などを指定するなど、コンピュータに何をすべきか、どのように行うかを明示的に指示することです。
C#:
for (int i = 0; i < 10; i++)
{
System.Console.WriteLine("Hello World!");
}
宣言型とは、コンピューターに何をすべきかを指示するときですが、実際にはどのように実行するかではありません。Datalog / Prologは、この点で頭に浮かぶ最初の言語です。基本的にすべてが宣言型です。あなたは本当に注文を保証することはできません。
C#ははるかに命令型のプログラミング言語ですが、Linqのように、特定のC#機能はより宣言型です。
dynamic foo = from c in someCollection
let x = someValue * 2
where c.SomeProperty < x
select new {c.SomeProperty, c.OtherProperty};
同じことを強制的に書くことができます:
dynamic foo = SomeCollection.Where
(
c => c.SomeProperty < (SomeValue * 2)
)
.Select
(
c => new {c.SomeProperty, c.OtherProperty}
)
(ウィキペディアLinqからの例)
ここでフィリップロバーツから盗む:
- 命令型プログラミングは、マシンに何かを行う方法を指示します(あなたが起こりたいことをもたらします)
- 宣言型プログラミングは、あなたが何をしたいのかをマシンに伝えます(そしてコンピューターはそれを行う方法を理解します)
2つの例:
1.配列内のすべての数値を2倍にする
必須:
var numbers = [1,2,3,4,5]
var doubled = []
for(var i = 0; i < numbers.length; i++) {
var newNumber = numbers[i] * 2
doubled.push(newNumber)
}
console.log(doubled) //=> [2,4,6,8,10]
宣言的に:
var numbers = [1,2,3,4,5]
var doubled = numbers.map(function(n) {
return n * 2
})
console.log(doubled) //=> [2,4,6,8,10]
2.リスト内のすべてのアイテムを合計する
必然的に
var numbers = [1,2,3,4,5]
var total = 0
for(var i = 0; i < numbers.length; i++) {
total += numbers[i]
}
console.log(total) //=> 15
宣言的に
var numbers = [1,2,3,4,5]
var total = numbers.reduce(function(sum, n) {
return sum + n
});
console.log(total) //=> 15
命令型の例では、新しい変数を作成し、それを変更して、その新しい値を返す方法(つまり、何かを実現する方法)が含まれますが、宣言型の例では、特定の入力で実行され、初期入力に基づいて新しい値が返されます(つまり、 、私たちが何をしたいのか)。
コンピュータサイエンスでは、宣言型プログラミングは、制御フローを記述せずに計算のロジックを表現するプログラミングパラダイムです。
http://en.wikipedia.org/wiki/Declarative_programmingから
一言で言えば、宣言型言語は、制御フロー(ループ、ifステートメントなど)の複雑さを欠いているため、より単純です。
良い比較は、ASP.Netの「分離コード」モデルです。宣言型の「.ASPX」ファイルと、命令型の「ASPX.CS」コードファイルがあります。スクリプトの宣言型の半分で必要なことをすべて実行できれば、実行されていることをより多くの人がフォローできることがよくあります。
命令型プログラミング
C/C ++、Java、COBOL、FORTRAN、Perl、JavaScriptなどのプログラミング分野を必要とするプログラミング言語。このような言語で書くプログラマーは、データ処理とプログラミングの知識に基づいて、問題を解決するために適切なアクションの順序を開発する必要があります。
宣言型プログラミング
従来のプログラミングロジックを記述する必要のないコンピューター言語。ユーザーは、C ++やJavaなどの手続き型プログラミング言語で必要なプログラムステップではなく、入力と出力の定義に集中します。
宣言型プログラミングの例は、CSS、HTML、XML、XSLT、RegXです。
命令型プログラミング-あなたは仕事をするコードを書く
宣言型プログラミング-他の誰かがその仕事をするコードを書く
私の理解では、どちらの用語も哲学にルーツがあり、宣言型および命令型の知識があります。宣言的知識は、真実の主張、数学の公理のような事実の陳述です。それはあなたに何かを教えてくれます。命令型または手続き的知識は、何かに到達する方法を段階的に説明します。それがアルゴリズムの本質的な定義です。もしそうなら、コンピュータプログラミング言語を英語と比較してください。宣言文は何かを述べています。退屈な例ですが、Javaで2つの数値が互いに等しいかどうかを宣言的に表示する方法は次のとおりです。
public static void main(String[] args)
{
System.out.print("4 = 4.");
}
一方、英語の命令文は、命令を出すか、ある種の要求をします。したがって、命令型プログラミングは単なるコマンドのリストです(これを実行し、それを実行します)。Javaで、ユーザー入力を受け入れているときに2つの数値が互いに等しいかどうかを表示するための必須の方法は次のとおりです。
private static Scanner input;
public static void main(String[] args)
{
input = new Scanner(System.in);
System.out.println();
System.out.print("Enter an integer value for x: ");
int x = input.nextInt();
System.out.print("Enter an integer value for y: ");
int y = input.nextInt();
System.out.println();
System.out.printf("%d == %d? %s\n", x, y, x == y);
}
基本的に、宣言的知識は特定の要素をスキップして、それらの要素を抽象化するレイヤーを形成します。宣言型プログラミングも同じです。
宣言型プログラムは、多かれ少なかれ「普遍的な」必須の実装/vmの単なるデータです。
利点:ハードコードされた(およびチェックされた)形式でデータのみを指定する方が、命令型アルゴリズムのバリアントを直接指定するよりも簡単で、エラーが発生しにくくなります。一部の複雑な仕様は、直接書き込むことができず、一部のDSL形式でのみ記述できます。DSLデータ構造で使用される最良の頻度は、セットとテーブルです。要素/行の間に依存関係がないためです。依存関係がない場合は、変更の自由とサポートの容易さがあります。(たとえば、モジュールとクラスを比較してください。満足しているモジュールと、脆弱な基本クラスの問題があるクラスと比較してください)宣言性とDSLのすべての商品は、そのデータ構造(テーブルとセット)の利点からすぐに得られます。もう1つのプラス-DSLが多かれ少なかれ抽象的である(うまく設計されている)場合は、宣言型言語vmの実装を変更できます。たとえば、並列実装を行います。
マイナス:あなたは正しいと思います。一般的な(そしてDSLによってパラメータ化された)命令型アルゴリズム/ vmの実装は、特定のものよりも遅くなったり、メモリを大量に消費したりする可能性があります。ある場合には。そのようなケースがまれな場合は、それを忘れて、ゆっくりさせてください。頻繁に使用する場合は、その場合はいつでもDSL/vmを拡張できます。どこか他のすべてのケースを遅くします、確かに...
PSフレームワークは、DSLと命令の中間にあります。そして、すべての中途半端な解決策として...それらは利益ではなく欠点を組み合わせています。彼らはそれほど安全ではなく、それほど速くはありません:)何でも屋のhaskellを見てください-それは強力なシンプルなMLと柔軟なmetaprogPrologの中間です...それはなんとモンスターなのでしょう。Prologは、ブール値のみの関数/述語を持つHaskellと見なすことができます。そして、その柔軟性がHaskellに対してどれほど単純であるか...
モバイルアプリ開発の観点から別の例を追加するだけです。iOSとAndroidには、アプリのUIを定義できるインターフェイスビルダーがあります。
これらのビルダーを使用して描画されたUIは、本質的に宣言型であり、コンポーネントをドラッグアンドドロップします。実際の描画は、フレームワークとシステムの下で行われ、実行されます。
ただし、コンポーネント全体をコードで描画することもできます。これは本質的に不可欠です。
また、Angular JSのようないくつかの新しい言語は宣言型のUIの設計に焦点を合わせており、同じサポートを提供する他の多くの言語が見られるかもしれません。Javaのように、JavaSwingまたはJavaFXでネイティブデスクトップアプリを描画するための優れた宣言型の方法はありませんが、近い将来、そうなる可能性があります。
なぜC#の宣言型プログラミングツールとして属性クラスについて言及している人がいないのか疑問に思います。このページの一般的な回答は、宣言型プログラミングツールとしてのLINQについて話しているところです。
ウィキペディアによると
一般的な宣言型言語には、データベースクエリ言語(SQL、XQueryなど)、正規表現、論理プログラミング、関数型プログラミング、および構成管理システムの言語が含まれます。
したがって、関数構文としてのLINQは間違いなく宣言型のメソッドですが、構成ツールとしてのC#の属性クラスも宣言型です。詳細を読むための良い出発点は次のとおりです。C#属性プログラミングの概要
すでにたくさんのコード例が追加されているので、別のコード例は追加しません。
代わりに、2つのアプローチの違いを、それらの本質が浮かんでいるほとんどの定義よりも明確になると思う方法で説明しようと思います。
宣言型アプローチは、特定のアルゴリズムの目的に焦点を合わせており、アルゴリズム自体を隠すことがよくあります。
命令型のアプローチは、特定の目的のためのアルゴリズムに焦点を合わせており、それはしばしば目的自体を隠します。
べき等と可換に基づいて、宣言型と命令型を区別する方が簡単であることがわかりました。それらについて知るために参照を使用してください。
この簡略化されたバージョンをチェックアウトして、べき等について知ってください。
次に、「WHAT」と「HOW」の定義を取り入れて、 「WHAT」と「HOW」が実際に何を意味するのかを理解します。宣言型では、データ間の関係を定義することにより、あるデータを別のデータに接続します。あなたはその関係がどのように達成されるべきかについて言及するのではなく、その関係が何であるかについて言及します。関係を通じて、この出力データを実現するための「方法」ではなく、出力データがどのように見えるかを説明します。
頭の中でいくつかの図を描き始め、いくつかの点(データ)を描き、それらを線で接続します(関係)。1対多、多対1、1対1のすべての可能な方法で描画します。このように、これらの線に矢印を付けます<-----------。特定のデータの基になっているすべてのデータを最初に計算してから左に移動してその特定のデータを計算する必要があるため、すべての矢印は左を向いている必要があります。
データa
がデータに基づいている場合b
、データc
とデータd
は他のデータに基づいている可能性があります。次に、、b
をc
最初d
に計算する必要があり、それからのみa
計算されます。つまりa
、線の左側にあり、他のすべては右側にあります。、、a
のそれぞれから1本に達する3本の線があります。b
c
d
この図にはいくつかのプロパティがあります。
- 他のすべてのデータとの関係に違反するデータはありません
- もちろん
b
、制御フローc
や順序は重要ではなく、d
前に計算する必要がありますが、の間にa
優先順位はありません。つまり、これら3つのうちどちらを最初に計算するかは関係ありません(可換)b
c
d
a
にのみ基づいておりb
、他の誰にも基づいていません。したがって、を使用して計算し、実行する関係演算を何回実行しても、同じことが達成される必要があります(べき等)。ここでのリレーションシップ操作の最終結果です。基本的に、影響を与えるすべての人は、を指す線を持っている必要があります。c
d
a
b
c
d
a
a
a
a
これらの関係(線)は関数(数学の関数であり、プログラミングではない)のようなものです。間違いなく、機能的なプログラミングは学界の人々の間で有名です。純粋関数(私たちのプログラミングの、したがって太字ではない)は、(数学の、したがって太字で)関数のようなものです。
これまでに、宣言型は、はいの場合はGOODで、いいえの場合は、PUREおよびIMMUTABLE(Function al Programmingで一般的に使用されます)のように聞こえ始めた可能性があります。それはここでの目的ではないので、それはこのパターンから自動的に浮かび上がったものです。
コードをこの図に変換できる場合は、それ以外の場合は完全に宣言型です。それ以外の場合は、スケールの別の場所にあります。
宣言型は数学に近いです。
次に、これらの関係(線)を拡大して、プログラムの実行中にコンピューター内で何が起こっているかを確認します。
命令が入ります。ここで、その地上レベルの作業が行われます。必須では、ステップバイステップで「どのように」実行する必要があるかを説明し、この一連のステップによって、あるデータ(入力b
c
d
)と別のデータ(出力a
)の間に要求された関係が作成されることを知っています。ここでは、変数を作成し、それらを変更し、配列をループし、他のすべてのものをループします。
命令法はプログラミングに近いです。
プログラムを宣言型または命令型と言うのではなく、左端が完全に宣言型で、右端が完全に命令型であるスケールで見たいと思います。宣言型は命令型の上に構築されていることを忘れないでください。したがって、表示される宣言型のものは実際には命令型の下にあります。一般に、プログラムは宣言型と命令型の混合です。
それでは、次の2つの例を見てみましょう。
2番目の例は、次のような図に変換できます。
reduce_r
map_r
filter_r
a
<--------- b
<--------- c
<--------- d
- filter_r(関係):
c
偶数のみd
- map_r(関係):
b
すべての数値に10を掛けたものc
- reduce_r(関係):
a
すべての数値が追加されますb
これは数学の複合関数reduce_r
のように見えるはずです: ( map_r
(filter_r
(d
)))
宣言型では、開発者の仕事は、最終的な目標(a
)をサブ目標( b
、 )に分割c
することです。これは、最終的な目標を達成するのに役立ちます。
もちろん、プロシージャマップの内部では、reduceとfilterは必須のコード実行です。
思考の糧map
:コードを期待どおりに機能させるために関数を左から右に移動することを前提とする必要がある場合、実際には宣言型の名前で命令型を実行しています。