コードスニペットで使用されているプログラミング言語を検出するための最良の方法は何でしょうか。
17 に答える
スパムフィルターで使用されている方法は非常にうまくいくと思います。スニペットを単語に分割します。次に、これらの単語の出現を既知のスニペットと比較し、関心のあるすべての言語について、このスニペットが言語Xで記述されている確率を計算します。
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
基本的なメカニズムがあれば、新しい言語を追加するのは非常に簡単です。新しい言語でいくつかのスニペットを使用して検出器をトレーニングするだけです(オープンソースプロジェクトにフィードすることもできます)。このようにして、「システム」がC#スニペットに表示され、「プット」がRubyスニペットに表示される可能性が高いことを学習します。
私は実際にこの方法を使用して、フォーラムソフトウェアのコードスニペットに言語検出を追加しました。あいまいな場合を除いて、100%の時間で機能しました。
print "Hello"
コードを見つけましょう。
コードが見つからなかったので、新しいコードを作成しました。少し単純ですが、私のテストでは機能します。現在、Rubyコードよりもはるかに多くのPythonコードをフィードすると、次のように表示される可能性があります。
def foo
puts "hi"
end
Pythonコードです(実際にはRubyですが)。これは、Pythonにもdef
キーワードがあるためです。したがって、Pythonで1000倍、Rubyで100倍を見def
た場合でも、 Ruby固有であるdef
にもかかわらずputs
、 Pythonと表示される可能性があります。end
これを修正するには、言語ごとに表示される単語を追跡し、どこかでそれで除算します(または、各言語で同量のコードをフィードします)。
お役に立てば幸いです。
class Classifier
def initialize
@data = {}
@totals = Hash.new(1)
end
def words(code)
code.split(/[^a-z]/).reject{|w| w.empty?}
end
def train(code,lang)
@totals[lang] += 1
@data[lang] ||= Hash.new(1)
words(code).each {|w| @data[lang][w] += 1 }
end
def classify(code)
ws = words(code)
@data.keys.max_by do |lang|
# We really want to multiply here but I use logs
# to avoid floating point underflow
# (adding logs is equivalent to multiplication)
Math.log(@totals[lang]) +
ws.map{|w| Math.log(@data[lang][w])}.reduce(:+)
end
end
end
# Example usage
c = Classifier.new
# Train from files
c.train(open("code.rb").read, :ruby)
c.train(open("code.py").read, :python)
c.train(open("code.cs").read, :csharp)
# Test it on another file
c.classify(open("code2.py").read) # => :python (hopefully)
他の人が解決した言語検出:
Ohloh のアプローチ: https://github.com/blackducksw/ohcount/
Github のアプローチ: https://github.com/github/linguist
別の方法は、highlight.jsを使用することです。これは、構文の強調表示を実行しますが、強調表示プロセスの成功率を使用して言語を識別します。原則として、どの構文ハイライトコードベースも同じように使用できますが、highlight.jsの良いところは、言語検出が機能と見なされ、テスト目的で使用されることです。
更新:私はこれを試しましたが、うまくいきませんでした。圧縮されたJavaScriptはそれを完全に混乱させました。つまり、トークナイザーは空白に敏感です。一般に、ハイライトヒットをカウントするだけでは、あまり信頼性が高くないようです。より強力なパーサー、またはおそらく一致しないセクション数の方がうまくいく可能性があります。
まず、言語の特定のキーワークを見つけようとします。
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
とても難しく、時には不可能です。この短いスニペットはどの言語のものですか?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(ヒント: 複数のうちのいずれかである可能性があります。)
さまざまな言語を分析してみて、キーワードの頻度分析を使用して決定を試みることができます。テキスト内で特定のキーワード セットが特定の頻度で出現する場合、その言語は Java などである可能性があります。 Java でキーワードとして使用すると、周波数分析がだまされます。
複雑さを一段階上げれば、構造を探すことができます。特定のキーワードが常に別のキーワードの後に来る場合は、より多くの手がかりが得られます。しかし、設計と実装がはるかに難しくなります。
使用しているスニペットのタイプによって異なりますが、一連のトークナイザーを実行して、どの言語のBNFに対して有効であるかを確認します。
私が遭遇した最善の解決策は、Ruby on Rails アプリでlinguist gemを使用することです。それを行うための特定の方法のようなものですが、うまくいきます。これは@niscによって上で言及されましたが、それを使用するための正確な手順を説明します. (次のコマンド ライン コマンドの一部は ubuntu に固有のものですが、他の OS に簡単に変換できるはずです)
一時的にいじっても構わない Rails アプリがある場合は、そこに新しいファイルを作成して、問題のコード スニペットを挿入します。(レールがインストールされていない場合は、ここに良いガイドがありますが、ubuntuの場合はこれをお勧めします。次に、実行rails new <name-your-app-dir>
してそのディレクトリに移動します。レールアプリを実行するために必要なものはすべてそこにあります)。
これを使用する Rails アプリを作成gem 'github-linguist'
したら、Gemfile に追加します (文字通りGemfile
、アプリ ディレクトリで呼び出されるだけで、ext はありません)。
次に ruby-dev をインストールします ( sudo apt-get install ruby-dev
)
次に、cmakeをインストールします(sudo apt-get install cmake
)
これで実行できますgem install github-linguist
(icuが必要であるというエラーが表示された場合は、実行しsudo apt-get install libicu-dev
て再試行してください)
sudo apt-get update
(上記が機能しない場合は、またはsudo apt-get install make
またはを実行する必要がある場合がありますsudo apt-get install build-essential
)
これですべてが設定されました。これで、コード スニペットを確認したいときにいつでも使用できます。テキスト エディターで、コード スニペットを挿入するために作成したファイルを開きます (スニペットapp/test.tpl
の拡張子がわかっている場合は、代わりにそれを使用します.tpl
。拡張子がわからない場合は使用しないでください)。 )。コード スニペットをこのファイルに貼り付けます。コマンド ラインに移動して実行bundle install
します (アプリケーションのディレクトリにある必要があります)。次に実行しますlinguist app/test.tpl
(より一般的にはlinguist <path-to-code-snippet-file>
)。タイプ、MIME タイプ、および言語がわかります。複数のファイル (または ruby/rails アプリでの一般的な使用)の場合bundle exec linguist --breakdown
、アプリケーションのディレクトリで実行できます。
特にレールをまだ持っていない場合は、多くの余分な作業のように思えますが、これらの手順に従う場合、実際にはレールについて何も知る必要はありません。ファイル/コード スニペットの言語。
素敵なパズル。
すべての言語を検出することは不可能だと思います。ただし、キートークンでトリガーすることはできます。(特定の予約語とよく使用される文字の組み合わせ)。
ベン同じような構文の言語がたくさんあります。したがって、スニペットのサイズによって異なります。
Prettify は、プログラミング言語を適切に検出する Javascript パッケージです。
http://code.google.com/p/google-code-prettify/
主に構文ハイライターですが、スニペットから言語を検出する目的で検出部分を抽出する方法があると思われます。
これを達成する簡単な方法はないと思います。私はおそらく、特定の言語/言語のクラスに固有の記号/共通キーワードのリストを生成します(たとえば、Cスタイル言語の場合は中括弧、BASIC言語の場合はDimおよびSubキーワード、Pythonの場合はdefキーワード、関数型言語の場合はletキーワード) 。その後、基本的な構文機能を使用して、さらに絞り込むことができる場合があります。
言語間の最大の違いはその構造だと思います。したがって、私の考えは、すべての言語に共通する特定の要素を見て、それらがどのように異なるかを確認することです. たとえば、正規表現を使用して次のようなものを選択できます。
- 関数定義
- 変数宣言
- クラス宣言
- コメント
- ループ用
- while ループ
- ステートメントを印刷する
そしておそらく、ほとんどの言語が持つべき他のいくつかのことです。次に、ポイントシステムを使用します。正規表現が見つかった場合、要素ごとに最大 1 ポイントを与えます。明らかに、一部の言語はまったく同じ構文を使用します (for ループは多くの場合、同じように記述されるfor(int i=0; i<x; ++i)
ため、複数の言語がそれぞれ同じことに対してポイントを獲得できますが、少なくとも完全に異なる言語である可能性を減らしています)。それらのいくつかは全面的に 0 のスコアを付けるかもしれません (たとえば、スニペットには関数がまったく含まれていません) が、それはまったく問題ありません。
これをジュールのソリューションと組み合わせると、かなりうまくいくはずです。余分なポイントとして、キーワードの頻度を探すこともできます。
面白い。さまざまな形式のテキストを認識する同様のタスクがあります。YAML、JSON、XML、または Java プロパティ? たとえば、構文エラーがあっても、JSON と XML を自信を持って区別する必要があります。
問題をどのようにモデル化するかが重要だと思います。マークが言ったように、単語のトークン化は必要ですが、おそらく十分ではありません。バイグラム、さらにはトライグラムが必要になります。しかし、私たちはプログラミング言語を見ていることを知っているので、そこからさらに先に進むことができると思います. ほとんどすべてのプログラミング言語には、シンボルとキーワードという 2 つの固有のトークン タイプがあることに気付きました。シンボルは比較的簡単に認識できます (一部のシンボルは言語の一部ではないリテラルである可能性があります)。次に、シンボルのバイグラムまたはトリグラムは、シンボルの周りに固有の構文構造を取得します。トレーニング セットが大きく、十分に多様である場合、キーワードはもう 1 つの簡単なターゲットです。便利な機能は、可能なキーワードの周りのバイグラムです。もう 1 つの興味深いタイプのトークンは、空白です。. 実際、空白で通常の方法でトークン化すると、この情報が失われます。プログラミング言語を分析するために、構文構造に関する有用な情報を運ぶ可能性があるため、空白トークンを保持します。
最後に、ランダム フォレストのような分類子を選択すると、github をクロールして、公開されているすべてのソース コードを収集します。ほとんどのソース コード ファイルは、ファイル サフィックスでラベル付けできます。ファイルごとに、空の行でランダムにさまざまなサイズのスニペットに分割します。次に、特徴を抽出し、ラベル付きのスニペットを使用して分類器をトレーニングします。トレーニングが完了したら、分類子の適合率と再現率をテストできます。
その単一のスニペットに基づいて、スニペットがどの言語に含まれているかを特定できる単一のソリューションはないと思います。キーワードを取りprint
ます。これは、さまざまな目的で使用され、さまざまな構文を持つさまざまな言語で表示される可能性があります。
アドバイスはあります。私は現在、プログラミング言語を識別するために使用できる、自分の Web サイト用の小さなコードを書いています。他のほとんどの投稿と同様に、聞いたことのない膨大な範囲のプログラミング言語が存在する可能性があり、それらすべてを説明することはできません.
私が行ったことは、キーワードの選択によって各言語を識別できるようにすることです。たとえば、Python はさまざまな方法で識別できます。確かに言語に固有の「特性」を選択すると、おそらく簡単です。Python の場合、コロンを使用して一連のステートメントを開始するという特性を選択します。これはかなりユニークな特性だと思います (間違っていたら訂正してください)。
def
私の例で、ステートメント セットを開始するコロンが見つからない場合は、キーワードを使用して関数を定義するとしましょう。def
Ruby はキーワードを使用して関数を定義するため、これはいくつかの問題を引き起こす可能性があります。2 つ (Python と Ruby) を区別するための鍵は、さまざまなレベルのフィルタリングを使用して最適な一致を得ることです。Ruby はキーワードend
を使用して関数を終了しますが、Python には関数を終了するものは何もなく、インデントを解除するだけですが、そこに行きたくありません。しかし、繰り返しend
ますが、ミックスに追加するさらに別のプログラミング言語である Lua を使用することもできます。
プログラミング言語が単純にオーバーレイしすぎていることがわかります。ある言語のキーワードである可能性のある 1 つのキーワードが、別の言語のキーワードである可能性があります。Java のように、よく一緒に使用されるキーワードの組み合わせを使用すると、public static void main(String[] args)
これらの問題を解消するのに役立ちます。
すでに述べたように、最も可能性が高いのは、比較的ユニークなキーワードまたはキーワードのセットを探して、他のキーワードと区別することです。そして、あなたがそれを間違えたとしても、少なくともあなたは試してみました.
ランダムスクランブラーを次のように設定します
matrix S = matrix(GF(2),k,[random()<0.5for _ in range(k^2)]); while (rank(S) < k) : S[floor(k*random()),floor(k*random())] +=1;