1. ホーム
  2. programming-languages

[解決済み] スニペットからプログラミング言語を検出する【終了しました

2022-07-21 19:53:21

質問

コードのスニペットの中でどのプログラミング言語が使われているかを検出する最も良い方法は何でしょうか?

どのように解決するのですか?

スパムフィルタで使われている方法が非常に効果的だと思います。スニペットを単語に分割します。次に、これらの単語の出現を既知のスニペットと比較し、このスニペットが言語 X で書かれている確率を、関心のあるすべての言語について計算します。

http://en.wikipedia.org/wiki/Bayesian_spam_filtering

基本的なメカニズムがあれば、新しい言語を追加するのはとても簡単です。新しい言語のスニペット (オープン ソース プロジェクトを提供することもできます) をいくつか使って検出器を訓練するだけでよいのです。この方法では、C# のスニペットに "System" が、Ruby のスニペットに "puts" が表示される可能性があることを学習します。

私は実際にこの方法を使用して、フォーラム ソフトウェアのコード スニペットに言語検出を追加しました。それは、あいまいなケースを除いて、100% の確率で機能しました。

print "Hello"

コードを探します。

コードが見つからなかったので、新しいものを作りました。少し単純化されていますが、私のテストでは動作します。現在、RubyのコードよりもPythonのコードを多く与えると、このコードと言うことになりそうです。

def foo
   puts "hi"
end

はPythonのコードです(実際はRubyですが)。これは、Pythonが def というキーワードもあるからです。ですから、もし1000倍の def が Python で 1000 回、100 回 def が Ruby の場合、Python と表示されることがあります。 putsend は Ruby 固有のものです。言語ごとに見た単語を記録し、どこかでそれで割ることで解決できます(または、各言語で等量のコードを与えることで解決できます)。

お役に立てれば幸いです。

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)