この問題を具体的に解決するために、PQ-Gram ツリー編集距離近似の実装である jqgram を使用できますが、C# に移植したくない場合は Node.js を実行する必要があります。ただし、移植は非常に簡単なはずです...アルゴリズムはそれほど複雑ではありません。シンプルな美しさ。
https://github.com/hoonto/jqgram
この例は、おおよそのツリー編集距離を生成するために、子とラベルを処理する方法を示す DOM と Cheerio の例です。結果として 0 から 1 の間の数値が得られるので、それがパーセンテージの平等です。ただし、ゼロの値は必ずしも同じツリーを示すわけではなく、非常に似ていることを意味するだけであることに注意してください。DOM と DOM の比較、または Cheerio と Cheerio の比較も簡単に行うことができます。または、ライブラリ全体を使用することを心配する代わりに、Cheerio が使用する HTML 解析を使用することもできます (Cheerio は、箱から出してすぐに使用できるかなり高速なサーバー側の jQuery および DOM のようなものです。実装)。
したがって、明らかにこのソリューションは Node.js とブラウザーの JavaScript に固有のものですが、これらの課題は C#/.NET に移植するよりも簡単かもしれないと思います。
// This could probably be optimized significantly, but is a real-world
// example of how to use tree edit distance in the browser.
// For cheerio, you'll have to browserify,
// which requires some fiddling around
// due to cheerio's dynamically generated
// require's (good grief) that browserify
// does not see due to the static nature
// of its code analysis (dynamic off-line
// analysis is hard, but doable).
//
// Ultimately, the goal is to end up with
// something like this in the browser:
var cheerio = require('./lib/cheerio');
// The easy part, jqgram:
var jq = require("../jqgram").jqgram;
// Make a cheerio DOM:
var html = '<body><div id="a"><div class="c d"><span>Irrelevent text</span></div></div></body>';
var cheeriodom = cheerio.load(html, {
ignoreWhitespace: false,
lowerCaseTags: true
});
// For ease, lets assume you have jQuery laoded:
var realdom = $('body');
// The lfn and cfn functions allow you to specify
// how labels and children should be defined:
jq.distance({
root: cheeriodom,
lfn: function(node){
// We don't have to lowercase this because we already
// asked cheerio to do that for us above (lowerCaseTags).
return node.name;
},
cfn: function(node){
// Cheerio maintains attributes in the attribs array:
// We're going to put id's and classes in as children
// of nodes in our cheerio tree
var retarr = [];
if(!! node.attribs && !! node.attribs.class){
retarr = retarr.concat(node.attribs.class.split(' '));
}
if(!! node.attribs && !! node.attribs.id){
retarr.push(node.attribs.id);
}
retarr = retarr.concat(node.children);
return retarr;
}
},{
root: realdom,
lfn: function(node){
return node.nodeName.toLowerCase();
},
cfn: function(node){
var retarr = [];
if(!! node.attributes && !! node.attributes.class && !! node.attributes.class.nodeValue){
retarr = retarr.concat(node.attributes.class.nodeValue.split(' '));
}
if(!! node.attributes && !! node.attributes.id && !! node.attributes.id.nodeValue) {
retarr.push(node.attributes.id.nodeValue);
}
for(var i=0; i<node.children.length; ++i){
retarr.push(node.children[i]);
}
return retarr;
}
},{ p:2, q:3, depth:10 },
function(result) {
console.log(result.distance);
});