web-dev-qa-db-ja.com

自動プログラミング:コードを書くコードを書く

The Pragmatic Programmer を読んだ後、私が最も興味深いと思った引数の1つは、「コードを書くコードを書く」でした。

私はネット上でそれに関するいくつかの説明や記事を検索してみましたが、このテーマに関するいくつかの良い記事を見つけましたが、それでも特定のコード実装や良い例は見つかりませんでした。

それはまだそれほど一般的な議論ではなく、ドキュメントが不足している、またはそれほど多くの人々に受け入れられていないような気がします。それについてもっと知りたいと思います。

この件についてどう思いますか?それは本当にあなたの生産性を向上させるものですか?本、ブログ、スライドショーなど、このテーマに関する優れたリソースは何ですか?


一部のコード例は、その実装をよりよく理解できるようにしていただければ幸いです。


これは、メタプログラミング、生成プログラミング、コード生成など、さまざまな関連するプログラミング手法に関する主題の wikiページ です。

110
Jose Faeti

LISPの世界では、コードを記述するコードを記述するコードがよく見られます(以下同様)。したがって、適切なサイズのLISPまたはSchemeプロジェクトは、良いコード例として役立ちます。 Racket コンパイラとランタイムのソース、および Bigloo を確認することをお勧めします。これらのライブラリは素晴らしいものです。

生産性に関しては、私はほとんどすべての開発作業でメタプログラミングを主要な手法として使用しており、コードサイズの縮小と可読性の向上の両方で明らかに役立ちます。重要なのは ドメイン固有言語 を使用することであり、メタプログラミングはそれらを実装する最も効率的な方法の1つです。

49
SK-logic

私は少し先に進むことを好み、writeコードを書くコードを書く代わりに、オブジェクト、メソッド、関数を生成するコードを書く。これは、LISPマクロまたはRuby動的プログラム変更機能など)を使用して実現できます。

小さな違いは、自動的に生成されたソースファイルで終わらないことです。通常、これらのファイルは人間が読める形式ではなく、変更することもできません。制御できないものでコードベースを増やすという考えは好きではありません。

このテーマについて私が楽しんだ1冊の本は メタプログラミングRuby です(もし知っていればRuby言語))


コメント内の次の質問の後に編集します。

なぜ生成コードをコーディングする必要があるのに便利なのですか?ユーザー入力に応じてさまざまなものを生成できるコードを記述して、繰り返し再利用できるようにする必要がありますか?

まず、メタプログラミングは目標ではなく、ツールです。 「すばらしい」または「Xはすべての開発者が使用するべきだと言った」ため、メタプログラミングを使用しないでください。

メタプログラミングを使用する良い理由の1つは、コードで見つけた他の通常のプログラミング手法(継承、デザインパターンなど)では実現できない一般的なパターン(パターンを繰り返すもの)を一般化することです。

Jordanによる のように、1つの典型的な使用例はデータベース処理とORM(オブジェクト関係マッピング)です。もう一度、Rubyで、ORMに適用されるメタプログラミングの優れた例である ActiveRecord を確認する必要があります。

最後の注意として:

考えないでください「メタプログラミングを適用したいのですが、コードのどこに適用できますか?」.

Think「このパターンがコード全体で繰り返されているのがわかります。コードをより小さく再利用可能なものにリファクタリングする方法が見つかりません。多分メタプログラミングは私を助けることができますか?」

67
David

さらに良いのは、あなたのためにあなたのコードを書く誰かが書いたコードを使うことです。

コードの自動化は、一般的にORMやその他のデータベース相互作用コードに適しています。もちろん、繰り返しはありますが、同様のコード作成に適しています。

もちろん、似たようなクラスをたくさん作成している場合は、動的言語で同じことをもっと早く達成できたかもしれませんが、私は話を戻します。

これは多くの人に採用されていますが、コードジェネレーターというラベルの付いたソフトウェアをよく目にします。

CodeSmithやMyGenerationなどの企業や製品を参照するか、次のWikipediaの記事を参照してください: http://en.wikipedia.org/wiki/Comparison_of_code_generation_tools

19
Jordan

古典的な例の1つは、Lexとyaccです。その主な目的は、あらゆる種類のパーサーを作成するという面倒な作業を回避することです。その過程で、多くのルールと状態を備えた複雑なパーサーを構築するのがはるかに速くなり、独自にローリングする人々による予期しないミスもすべて回避されます。

これは、アセンブラーを作成するためのツールであるcの背後にある考え方でもあります。同じことは、名前を付けたい高水準言語にも当てはまります。コードを作成するツールには、いくつかの簡単なパラダイムがあります。

適切なIDEは、あなたの指先でのドキュメント、スマートな自動補完、コードスニペットを提供するのに役立ちます。IDEにはさまざまなテンプレートも含まれているため、プログラムを最初から開始する必要はありません。プログラムがあります。 UMLダイアグラムを取得し、高級言語でクラスを大まかに説明します。

最後に、問題セット内でコードを生成するための独自のツールを作成できます。これが、Lexとyaccが最初に始めた方法です。まさにこの理由で、あらゆる種類のドメイン固有の言語が存在します。理解しやすいコードでソリューションを説明するビルディングブロックをいくつか作成し、一般的なアクティビティ、または単純なコマンドで複雑なセクションをまとめます。あなたはevery問題の解決策を探しているのではなく、あなたが扱っている特定の問題のより簡単な定義です。

ある意味では、バイナリレイヤーの上で行うことはすべて、コードの自動化です。

16
Spencer Rathbun

メタプログラミング

メタプログラミングは、多くのショップで論争の的となっている手法です。その理由は、他の強力なツールと同様に、助けや傷の規模が大きいためです。

長所

  • より表現力が高く、記述および維持するコードが少ない(多くの場合、1桁以上)
  • コードで解決する問題のクラス全体での一貫性、より一貫した動作
  • 生産性、より大きな問題空間の解決策のためのより少ないコード

短所

  • 複雑さ、コードが少なくても非常に複雑になる可能性がある
  • 安全性、時々型安全性、静的解析は一般的に犠牲になります
  • バグはさらに影響し、小さなエラーはより大きな影響を与えます

私は巨大なメタプログラミングのファンですが、長い間やっています。私にとっては、リスクを補う以上に、コードサイズの縮小と一貫した動作のトレードオフです。コードが少ないということは、バグが少なく、保守するコードが少ないことを意味します。通常、大規模な機能を非常に迅速に追加できます。

しかし、これはすべてのプログラマーがそれに取り組むべきだと私が思うことを意味するものではありません。私はメタプログラミングによって生じた大きな問題を見て、修正しなければなりませんでした。通常は、概念を理解していない人が機能を拡張しようとしたり、バグを修正したりしたときからです。それは、最低限の詳細志向である特定のマインドセットを必要とします。 メタプログラミング手法を使用するための質問は、チームの決定である必要があります。理解できないチームメンバーがいる、気質がない、またはそれに反対している場合は、どのチームもメタプログラミングを使用するべきではありません。

13
dietbuddha

ほとんどのコードはコードを記述します。たとえば、phpコードはhtmlの記述に役立ちます。 php pdoライブラリーは、SQL呼び出しの作成に役立ちます。ファイルI/O関数は、OSと通信するためのコードを記述します。通常の関数呼び出しでさえ、実行される別のコードブロックへの参照です。したがって、関数呼び出しはコードを記述しています。

大まかに言えば、コンピューティングは、ハードウェアに配線されたコードの物理的な現実にぶつかると終了するスタックを再帰的に形成するコードを書くコードを書くと考えることができます。

9
Ben Haley

私たちの会社では、インターネットからダウンロードしたデータを使用して実際にC++またはC#クラスを生成するいくつかのツールを使用しています。これらのクラスはデータコンテナであり、多数のオブジェクトがリストに含まれています。

5
Holli

メタプログラミングは長い間プログラミングの一部でした。コードを作成するSWIGやWYSIWYGデザイナーなどのツールだけでなく、Cのプリプロセッサなどの言語ツールや、C++のテンプレートやC#/ Javaのジェネリックなども考慮してください。

実際、すべてのコンパイラは単なる別のメタプログラムであり、プログラムテキストと出力マシンまたはVMコードを取得します。そして、コンパイラのない人生ですか?.

5
DeadMG

これが私の過去の具体例です。

データアクセスにBDEを使用して、約50MBのDelphiソースコードがあるサイトで働いていました。彼らは、Direct Oracle Accessを使用して、BDEでサポートされている最も高いバージョン(正しく思い出せば8i)を超えてOracleをアップグレードできるようにしたいと考えていました。

したがって、コーダーのチームにすべてのフォームとデータモジュールを処理させ、すべてのコンポーネントを手動で変更させる代わりに、次のようなPerlスクリプトを作成しました。

  1. DFM(フォームファイル)を解析し、すべてのTQuery、TTable、TStoredProcedure、およびTDatabaseオブジェクトを識別-アイテムをリストに格納。

  2. PAS(コード)を解析し、オブジェクトの使用法を識別しました-TQueriesは更新または選択を行っていましたか?また、IDEのフォームにドロップされるのではなく、コードで作成されたオブジェクトを識別しました。

  3. オブジェクトタイプを適切に変更するDFMとPASを書き直し(たとえば、SQLプロパティを「select * from」に設定したTTable-> TOracleDataSetなど)、メソッドを呼び出しました。また、パラメーターを閉じる、開く、設定するのに適切な場合は、追加のメソッド呼び出しが追加されました。

要するに、5名以上の開発者が6か月間働いていたという当初の見積もりではなく、3週間でスクリプトを調整して、さまざまなコーディングスタイルのさまざまなチームによって作成されたさまざまなアプリケーションで作業しました。

そして、私がそのアプローチを使用することさえ考えた理由は、読書を通してでしたThe Pragmatic Programmer

5
mcottle

これを行う方法は、要件によって異なります。静的コード生成を使用している場合、すべてのインフラストラクチャを自分で作成するか、CodeSmithやMyGenerationなどの既存のジェネレーターを使用できます。これらを使用して、必要なテンプレートを作成するだけです。

これに関する私の最後のプロジェクトは、いくつかの基本的なASP.NET CRUD画面でした(コード生成はこれに適しています)。プロセスでは、エンティティをXMLファイルのメタデータとして定義しました。テンプレートを作成して、必要なさまざまなアーティファクト(エンティティークラス、リポジトリ、サービスクラス、asp.netコントロール、asp.netページなど)をカバーします。生成プロセスを実行し、出力のスタイルを設定します。

テンプレートの作成にはある程度のオーバーヘッドがありますが、それらは後続の同様のプロジェクトで再利用できます。同様に、基になるデータへの変更は、メタデータを変更して生成を再実行することで処理され、変更をより簡単かつ迅速に実装できます。

テストについても。これはテンプレート化されたシステムであるため、プロセスの出力を最初に検証するのにある程度の時間を費やす必要があります。テンプレートが間違っている場合、そのテンプレートからのすべての出力も同様に間違っています。これに満足したら、コードジェネレーターを使用してxmlメタデータから基本的なテストを作成し、それを拡張して特殊なケースをカバーすることもできます。ただし、特定のことに対応するためにコードテストを手動で処理する必要がある場合があることを忘れないでください。コード生成によって作業が減り、完全になくなるわけではありません。

5
CdMnky

あなたは例を求めます...

SQLを使用する場合は、データベースを直接変更するのではなく、データベースの構造変更(テーブル、列、主キー、制約などの追加)を含め、必要な変更を行うスクリプトを実行する必要があります。 。多くの場合、多くのテーブルまたは列に対して同じアクションを同時に実行する必要があり、それらを1つずつ実行するのは面倒であり、必要なことを実行するより大きなスクリプトを出力する短いスクリプトは現実になる可能性がありますタイムセーバー。

たとえば、DATEデータ型がMS SQLサーバーに導入される前は、日付列の唯一の選択肢はDATETIMEでしたが、これには時間の部分があり、データの処理が少し難しくなります。日付データタイプのバージョンにアップグレードする場合、時刻が常に00:00である列を更新することができます。数十または数百のDateTime列を含むデータベースでは、これにはかなりの時間がかかります。しかし、すべてのテーブルをクエリするスクリプトを記述して、DATETIMEのデータ型ですべての列をチェックし、時刻が00:00以外であるかどうかを確認し、そうでない場合は変更するテーブル/列のALTERステートメントを作成します。データ型をDATEにします。プレスト、コードを書くコード。

4
jmoreno

ここでの多くの回答は、一般にメタプログラミングと呼ばれるものを指しますが、実際には、AIに関連する自動プログラミングと呼ばれるプログラムに関するフィールド理解または合成プログラム[1]。

すべてのコンパイラー(またはメタプログラム、コードジェネレーター、トランスレーター、マクロシステムなど)は変換を処理し、変換の固定アルゴリズムを実行することによって入力から出力を生成します。しかし、従来のコンパイラーまたはメタプログラムは、リストの並べ替えの定義、説明、または例([5、3、9] => [3,5,9]など)が与えられていないと、並べ替えアルゴリズムを作成しません。このような「自動プログラミング」分野の関心のある問題。

[1]-プログラム理解システムの進捗レポート ftp://db.stanford.edu/pub/cstr/reports/cs/.../CS-TR-74-444.pdfShare

3
Thiago Silva

私はPHPモジュールを持っています。これは、HTMLを生成するJavaScriptコードを含むWebページを出力します。それはすぐそこに3つのレイヤーがあります。男の子はとても読みづらかったです!

プログラミングクラスでは、ユーザーから数式文字列を取得して解析し、値を表示するプログラムを作成する必要がありました。最も印象的なソルバーは、ユーザー入力を受け取り、それをmain(){printf( "%d"、...);}にラップして、スクリプトを実行して、コンパイル、リンク、および実行しました。彼はパーサーを書きませんでした!今日では、SQL SELECTステートメントでそれを行うことができます。

それはあなたが遊んで、それが便利になる将来の日のためにそれを保管しておくべきツールです。

3
Andy Canfield

私はきちんとしたmeta-programmingソリューションをPrologで開発しました。メインアプリケーション(C++で言う)は、実行時に問題の抽象的な定義をPrologアプリケーションに変換し、次に委任します。多くの場合、C++で同等の機能を作成するには時間がかかります。

このシナリオはcode-writing-code引数を支持する優れたケースだと思います。

3
user16410

この件についてどう思いますか?

メタプログラミングは、非動的言語に最も一般的に関連付けられています。これは、非生産的でインテリジェントではないコード行をたくさん使わずに、特定の動作(ORMの実装など)を達成するのが難しいためです。

しかし、PHPなどのより動的な言語であっても、コード生成は本当に人命を救うことができ、生産性を大幅に向上させることができます。最近のフレームワークでは、宣言する特定のビジネスオブジェクトの一般的なモデル、フォーム、テスト、およびアクションのほとんどを生成するスキャフォールディングを使用することが非常に一般的です。これがsymfonyやRoRなどのフレームワークが非常に成功する理由の1つです。これらのコード生成ツールは、一貫したコードを非常に迅速に作成し、プログラマーの生産性を向上させます。

ウェブサイトでは、インタラクションのほとんどが4つの主なアクションを中心に展開します。

  • 要素を作成する
  • 要素のセットを取得します(可能なフィルタリングを使用)
  • 新しい属性で要素を更新する
  • 要素のセットを削除する

少なくともこの4つの主要なアクションを中心に展開するすべてのことは可能であり、コード生成ツールを使用して最大の生産性を実現するために私見を実現する必要があります。

私の会社では、symfonyを使用しており、そのadmin-generatorは例外的なツールであり、実行時にコードを生成する(そしてそれをキャッシュする)だけです。つまり、タスクや外部ツールを使用して、新しいコードを生成するには、キャッシュを消去するだけです。この種のツールをCRUD操作に使用することを強くお勧めします。

しかし、symfonyの素晴らしいコントリビューターが行ったことを行うのは簡単な作業ではありません。私はいくつかのコード生成タスクを自分で実装しましたが、真に一貫していて、ほとんどのコーナーケースをカバーする幅広い実装で何かを行うのは簡単ではありません。

それは本当にあなたの生産性を向上させるものですか?

メタプログラミングは、低レベルの作業(フレームワーク、キャッシング、コンパイラーなど)で非常に重要であると思いますが、ビジネス層で何かを行う場合は、細心の注意を払ってアプローチする必要があります。

コード生成を使用することは、疑いもなく主要な生産性向上です。独自のコード生成ツールを実装します。フレームワークを自分で構築しているのでない限り、それほど多くはありません。

本、ブログ、スライドショーなど、このテーマに関する優れたリソースは何ですか?

プログラミングを理解するための最良のリソースは、常に良いコメント付きのソースコードです。 RubyOnRailsSymfony adminジェネレーターを調べることは良いアイデアだと思います。

3
luisfaceira

CL(Common Lips)マクロをご覧ください。私の意見では、それはまさにあなたが望むものです。唇はメタプログラミングに最適です。

また、完全なメタプログラミングサポート(マクロを含む)を備えた.NETパワーを使用したい場合は、 Nemerle をお勧めします

しかし、真のコード生成エンジンが必要な場合は、 Apache thrift を確認してください。

3
cnd

私はそのようなツールに取り組んでいます。この特定のケースでは、データベース内の関数のシグネチャに基づいて、データレイヤーに基づいたVB.NETコードを生成します。

作業の開始オンおよびコード生成では、コードの生成方法がわからないため、最初は困難ですが、ルールのセットを確立すると、生成する必要のあるコードで常にそれらのルールに基づいて生成されるので、そのコードでの作業はそれほど難しくありません。もちろん、コード生成の複雑さとルールの数によっては、タスクがより困難になる可能性があります。ただし、本質的に、自動コード生成は反復的なコーディングタスクに使用され、高度に変化する高度なコードには使用されません。

Testing出力は2つあります。まず、コードがコンパイルされることを確認する必要があります。これは簡単です。次に、生成されたパラメーターに基づいて、出力が意図したとおりに機能することを確認する必要があります。その難しさは、生成するコードの複雑さによって異なります。

私が心からお勧めするのは繰り返しの方法でコードを記述したい場合であり、時間に余裕があることです。生成されたコードでは実行できないことを考えてみてください。そうである場合(反復コードの場合は、ほとんどの場合に当てはまります)、何回拡張する必要があるかを考え、そのコードを少し変更し、その正確な種類のコードを何回も作成する必要があるかを検討します。これらのいずれかに対する答えが「多数」である場合、そのコードのジェネレータを作成することを真剣に検討する必要があります

それが役に立てば幸い
IPP

3
Ioan Paul Pirau

メタプログラミングは維持するのが非常に難しい場合があります。最初は見た目はエレガントに見えますが、コーナーケースに遭遇すると、エラーが(生成されたコードで)遅れてキャッチされ、全体が使用/デバッグの悪夢になります。

私は主にpython=コードを記述しましたが、私の経験では、この言語ではメタプログラミングは常に悪い選択です。退屈な通常の言語機能でそれを行うためにいつでもリファクタリングできます。結果のファンキー性は低くなります、しかしより住みやすい。

2
Simon Bergot

MITコース6.916の革新的なWebサービスのソフトウェアエンジニアリング( http://philip.greenspun.com/teaching/psets/ps4/ps4.adp )。

その目的は、「メタデータの美徳を生徒に教えます。具体的には、Webサービスの要件を正式に表現し、そのサービスを実装するコンピュータープログラムを生成するコンピュータープログラムを構築する方法を学びます。」

これは、潜在的なArsDigita( http://en.wikipedia.org/wiki/ArsDigita )の新入社員が最初のバブルの間に解決する必要があった問題セットの1つです。

「SQL for Web Nerds」の本、Philipがpsetで参照しているものは( http://philip.greenspun.com/sql/ )に移動しました。

2
espeed

OPはリソースを要求します。

DMS Software Reengineering Toolkit は興味深いかもしれません。これは純粋なメタプログラミングツールであり、カスタムプログラム分析および変換ツールを構築できるようにすることを目的としています。

[OPの質問へのコメントに従うために、特定の変換ツールを構築するために使用される場合、DMSはコードを記述し、コードを記述する製品ラインです:]

DMSは、ターゲットプログラミング言語にとらわれない(ただし独立ではない)ことでこれを実現します。 DMSは、OSが標準プログラミングタスクにさまざまなサービスを提供するのと同じように、さまざまなメタプログラミングタスクに必要な標準サービスを提供します。これらのサービスには、強力な解析、抽象構文ツリーの自動構築、ツリーでのパターンマッチングと書き換え、多重継承、制御フロー、データフロー、ポイントツーコールなどの厄介なスコープルールで言語を簡単に管理できるシンボルテーブルライブラリが含まれますグラフ分析。処理する特定の言語がない場合、これは意味がありません。そのため、DMSはこれらの一般的な機構に関連付けられた言語定義を受け入れ、言語固有の解析を行いますAST構築、ターゲット言語-ターゲット言語構文、ターゲット言語固有のアナライザーなどを使用した特定のパターンマッチング/書き換え.

OSと同様に、DMSは、どの(メタ)プログラムを記述したいかについての意見や制約がほとんどないように設計されています。つまり、メトリックの抽出、デッドコードの検出、アスペクトウィーバーの実装、翻訳など、さまざまな目的に使用できます。言語、DSLからのコードの生成、大規模アプリケーションの再設計。 (DMSはこれらすべてのタスクですでに使用されています)。

言語リファレンスマニュアルのすべてをエンコードすることに時間をかけたくない場合は、堅牢な言語定義が必要です(JavaおよびC++の意味を考えてください)。DMSはこの問題を解決します利用可能な完全な言語定義のライブラリ。ここでの類似点は、OSにデータベースを使用できるようなものです。データベース中心のアプリケーションを作成するために、それらのいずれかを実装する必要はありません。

2
Ira Baxter

2001年頃に、ビジネスオブジェクトとデータオブジェクトを広範囲に使用するプロジェクトに取り組み始めました。フロントエンドのWebサイトを構築する予定でしたが、ビジネスレイヤーとデータアクセスレイヤーが完全に開発されていなかったため、親指をいじくり回していました。それから数週間後、私はそれらのレイヤーが何をしていたのかをしっかりと見始めました。基本的に、それらはストアドプロシージャから返されたデータを、データのフィールドに対応するプロパティを持つオブジェクトのコレクションとして公開するか、入力パラメーターを取り、それらをストアドプロシージャに送信してデータベーステーブルに保存していました。 2つのレイヤー間で多くのシリアル化/非シリアル化が行われ、Microsoft Transaction Serverが関与し、IDL/ODLタイプライブラリがありましたが、すべてパターンに適合しています。

2週間後、IDL/ODLをダンプし、ビジネスオブジェクトとデータオブジェクトもダンプするコードジェネレーターを作成しました。ビジネスレイヤーとデータレイヤーのオブジェクトを構築するのに2年かかり、これらのオブジェクトのデバッグとテストができるようになりました。コード生成で2週間で同じ出力が得られましたが、すべて生成されたため、バグはほとんど発生しませんでした。

そのコードジェネレーター(下位レベルのCASEツール)は、約8〜10年間、さまざまな反復を繰り返してきました。これは、原則が非常に単純だったためです。データベースと通信するときに実行する必要があることを実行しているので、多くの反復的なコーディング、そしてそれが正しくなれば、もう心配する必要はありません。

そのため、はい:コードジェネレーターを使用します。特に、コーディングが反復的で、明確に定義されたパターンに適合する場合に使用します。

RegXマクロを使用して同様のことをしたり、Excelの数式を使用して同様のことをしたりする人は知っています(これも私が行います)。

2

メタプログラミングの例

Ruby Authority と呼ばれる認証ライブラリ)があります。これにより、開発者はcurrent_user.can_read?(@post)@post.readable_by?(current_user)などのメソッドを使用してアプリで質問できます。これらの質問には、中央集中型の承認者クラスが回答します。

これは重要な部分です:ユーザーの設定を見るまで、権限はどのメソッドを定義するかわかりません。ユーザー構成には以下が含まれる場合があります。

config.abilities =  {
  ...
  :read      => 'readable',
  :microwave => 'microwavable',  # user-defined
  ...
}

その場合、current_user.can_microwave?(@post)のようなメソッドが必要です。

メタプログラミングはこれを可能にします。構成を読んだ後、私は知っています どのメソッドを定義するか

Authority.verbs.each do |verb|
  class_eval <<-Ruby, __FILE__, __LINE__ + 1 # allows for a Nice bracktrace
    def can_#{verb}?(resource)
      resource.#{Authority.abilities[verb]}_by?(self)
    end
  Ruby
end
2
Nathan Long