如何读取此基数树结构以确定下一个字符串概率?

时间:2015-02-16 04:20:17

标签: javascript patricia-trie

在JavaScript中,我试图获取给定的用户输入并猜测可能完成用户当前(不完整)键入的单词的3个最可能的单词。猜测基于用户的过去输入。我正在研究here, in this JSFiddle

我为记录用户过去输入而构建的结构是经过修改的Radix Tree (AKA Patricia Trie)

输入:" hey"

{
    "h": {
        "value": "h",
        "count": 1,
        "followables": {
            "e": {
                "value": "e",
                "count": 1,
                "followables": {
                    "y": {
                        "value": "y",
                        "count": 1,
                        "followables": {}
                    }
                }
            }
        }
    }
}

这种数据结构完美构建,我认为它是实现所述目标的最佳结构。我的问题是读取基数树数据的功能,以定义给定输入的3个最可能的单词。例如,在上面的数据中,如果用户输入" h",则猜测函数应返回如下对象:

guess : {
   1 : "hey",
   2 : "",
   3 : ""
}

所以这是我的代码/进度:

学习 - 获取完整的输入字符串并将组合整理到基数树(brain)中:

function learn(message, brain) {
    if (message.length == 0) return {}; // or do something else
    var ch = message[0]; // get the first character
    if (!brain[ch]) { // create new node when not exists
        brain[ch] = {
            value: ch,
            count: 1,
            followables: {}
        };
    } else { // increment count when exist
        brain[ch].count += 1;
    }
    var substr = message.substring(1); // remove first character
    if (substr) { // do it for the remaining substring
        brain[ch].followables = learn(substr, brain[ch].followables);
    } else {
        renderData();
    }
    return brain;
}

这一切都做对了。不幸的是,下一个代码,用于读取数据并猜测用户正在键入的单词,并不好。我对我的一个非常复杂的功能有困难。我把它分成了小功能,因为我学到的是最好的做法,但我担心我弄得一团糟可能会简单得多:

猜猜 - 参加"学习"字符串数据并对用户可能输入的单词进行3次猜测:

function guess(progress, brain) {
    console.log("Guessing based on: " + progress);
    var guesses = {
        0: "",
        1: "",
        2: ""
    }
    var firstChar = progress[0]; 
    if (brain[firstChar]) {
        var step = brain[firstChar];
        for (var i = 0; i < progress.length; i++) {
            var char = progress[i];
            if (step.followables[char]) {
                step = step.followables[char];
                if (i == progress.length) {
                    var guesses = nextStrings(step.followables);
                    renderGuesses(guesses);
                }
            } else {
                renderGuesses(guesses);
            }
        }
    } else {
        renderGuesses(guesses);
    }
}

function renderGuesses(guesses) {
    console.log(guesses);
    $('#guess-1').text(guesses[0]);
    $('#guess-2').text(guesses[1]);
    $('#guess-3').text(guesses[2]);
}

function nextStrings(followables) {
    console.log('Searching for next string...');
    var results;
    if (followables.length > 0) {
        results = chooseRoutes(followables);
    } else {
        results = {
            0: "",
            1: "",
            2: ""
        }
    }
    console.log(result);
    return result;
}

function chooseRoutes(followables) {
    var results = {
        0: {
            value: "",
            count: 0
        },
        1: {
            value: "",
            count: 0
        },
        2: {
            value: "",
            count: 0
        }
    };
    for (var i = 0; i < followables.length; i++) {
        var count = followables[i].count;
        if (count > results[0].count) {
            results[0].value = followStr(followables[i], "");
        } else if (count > results[1].count) {
            results[1].value = followStr(followables[i], "");
        } else if (count > results[2].count) {
            results[2].value = followStr(followables[i], "");
        }
    }
    console.log(results);
    return results;
}

function followStr(followables, str) {
    var guess = {
        value: "",
        count: 0
    };
    for (var i = 0; i < followables.length; i++) {
        if (followables[i].count > guess.count) {
            guess = followables[i];
        }
    }
    followables = guess.followables;
    if (guess.value != " ") {
        str += guess;
        followStr(followables, str);
    } else {
        console.log(str);
        return str;
    }
}

Sidenote - 虽然在字典上进行模糊字符串搜索是一种更常见的方法,但学习方法是一种很好的方法来定制用户的写作/消息风格的猜测并支持用户的非标准词汇(&#34; heyy&#34;,&#34; sup&#34;,&#34; :P &#34;,&#34; lol&#34;) - 这些猜测的结果可以与标准词典结果相结合(并优先考虑)。

1 个答案:

答案 0 :(得分:0)

您用于字典的结构不正确,它应该包含对象的数组。例如,输入以下单词后:

hi
hi
hi
hi
hi
hey
hello
hella

结构应该是:

history: [{
    letter: "h",
    count: 8,
    followables: [{
        letter: "e",
        count: 3,
        followables: [{
            letter: "y",
            count: 1,
            followables: []
        }, {
            letter: "l",
            count: 2,
            followables: [{
                letter: "l",
                count: 2,
                followables: [{
                    letter: "o",
                    count: 1,
                    followables: []
                }, {
                    letter: "a",
                    count: 1,
                    followables: []
                }]
            }]
        }]
    }, {
        letter: "i",
        count: 5,
        followables: []
    }]
}]

您创建和存储历史记录的方式(我会使用localStorage)对我来说并不感兴趣。专注于递归函数,深入挖掘树内部以获得建议。对于给定的单词,这一个得到最终followables

findChildren: function (node, depth) {
    /* Traverse history for current word, there is only one path */
    for (k in node) {
        if (node[k].letter == app.progress[depth]) {
            if (depth + 1 == app.progress.length) {
                /* Found it, hope it has followables */
                return node[k].followables;
            } else {
                /* Must go deeper... */
                return app.findChildren(node[k].followables, depth + 1);
            };
        };
    };
    /* No results */
    return false;
}

和第二个(getWord)创建建议:

countWordsFromNode: function (node) {
    for (i in node) {
        if (node[i].followables.length) {
            app.countWordsFromNode(node[i].followables);
        } else {
            app.totalInNode++;
        };
    };
},
getWord: function (node, limit) {
    /* First sort by count */
    var sorted = node.sort(function (n1, n2) {
        return n2.count - n1.count;
    });
    for (k in sorted) {
        app.guesses[app.totalFound].word += sorted[k].letter;
        if (sorted[k].followables.length) {
            app.totalInNode = 0;
            app.countWordsFromNode(sorted[k].followables);
            for (m = 1; m < app.totalInNode; m++) {
                if ((app.totalFound + m) < limit) {
                    app.guesses[app.totalFound + m].word += sorted[k].letter;
                };
            };
            app.getWord(sorted[k].followables, limit);
        } else {
            /* End of word */
            app.totalFound++;
        };
        if (app.totalFound >= limit) {
            /* Found all suggestions */
            break;
        };
    };
}

您可以在 this Fiddle 中查看详细信息,我必须删除您的一些代码。将它集成到任何地方都很容易,例如,您可以设置其他建议字段,当前是:

guesses: [
    {
        element: $('#guess-1'),
        word: ''
    },
    {
        element: $('#guess-2'),
        word: ''
    },
    {
        element: $('#guess-3'),
        word: ''
    }
]

修改 修正了在右侧添加字母的错误。