Я думаю, что метод, используемый в фильтрах спама, будет работать очень хорошо. Вы разбили фрагмент на слова. Затем вы сравниваете вхождения этих слов с известными фрагментами и вычисляете вероятность того, что этот фрагмент написан на языке X для каждого интересующего вас языка.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Если у вас есть базовый механизм, то очень легко добавлять новые языки: просто обучите детектор с помощью нескольких фрагментов на новом языке (вы можете использовать его для проекта с открытым исходным кодом). Таким образом, он узнает, что «System», скорее всего, появится в фрагментах C #, а «put» - в фрагментах Ruby.
Я фактически использовал этот метод, чтобы добавить определение языка к фрагментам кода для программного обеспечения форума. Это работало 100% времени, за исключением неоднозначных случаев:
print "Hello"
Позвольте мне найти код.
Я не смог найти код, поэтому сделал новый. Это немного упрощенно, но это работает для моих тестов. В настоящее время, если вы добавляете в него гораздо больше кода Python, чем кода Ruby, вероятно, он скажет, что этот код:
def foo
puts "hi"
end
- это код Python (хотя на самом деле это Ruby). Это потому, что в Python тоже есть ключевое слово def
. Так что, если он видел 1000x def
в Python и 100x def
в Ruby, он все равно может сказать Python, даже если puts
и end
специфичны для 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)