Php 解析自然语言

Php 解析自然语言,php,javascript,jquery,regex,Php,Javascript,Jquery,Regex,开始:我知道这个系统会有缺陷 注意:我添加了一些其他语言,因为我没有发现这个问题特定于php。JavaScript或jquery解决方案可以工作……我可以更改语言……这就是我想要的方法 什么:我试图解析一个字符串以确定用户想要什么 这个想法是字符串是由语音生成的 例1: 打开厨房的灯,关掉卧室和客厅的灯 例2: 把厨房的灯打开,卧室的灯打开,客厅的灯关上 例3: 关掉厨房、卧室和客厅的灯 这是一个过于简化的示例,但请注意,我希望扩展到这三个房间之外,并且只控制灯光 示例:外部吊扇打开 如何:我目

开始:我知道这个系统会有缺陷

注意:我添加了一些其他语言,因为我没有发现这个问题特定于php。JavaScript或jquery解决方案可以工作……我可以更改语言……这就是我想要的方法

什么:我试图解析一个字符串以确定用户想要什么

这个想法是字符串是由语音生成的

例1: 打开厨房的灯,关掉卧室和客厅的灯

例2: 把厨房的灯打开,卧室的灯打开,客厅的灯关上

例3: 关掉厨房、卧室和客厅的灯

这是一个过于简化的示例,但请注意,我希望扩展到这三个房间之外,并且只控制灯光 示例:外部吊扇打开

如何:我目前正在使用一些while循环来迭代数组,并检查数组中是否存在某些字符串

更多操作方法:我的想法是首先在“和”上拆分字符串。然后,我检查每个数组的开或关。如果它没有开或关,我就用下一个来加入数组

帮助:我很想澄清这个概念,同时看看其他人的想法……我愿意做任何事情

谢谢 JT

代码:

$input = 'kitchen lights on and bed and living lights off'; 
$output = preg_split( "/ (and) /", $input );
$num = (int)count($output);
$i=0;

while($i<$num){
    if ((strpos($output[$i],'on') !== false)||(strpos($output[$i],'off') !== false)) {}
    elseif(((strpos($output[$i+1],'on') !== false)||(strpos($output[$i+1],'off') !== false))){
    $output[$i+1] .= ' + '.$output[$i];
        unset($output[$i]);

    }

    $i++;
}
$output = array_values($output);
$i=0;
$num = (int)count($output);
echo '<br>';
while($i<$num){
if ((strpos($output[$i],'lights') !== false)&&(strpos($output[$i],'on') !== false)&&(strpos($output[$i],'kitchen') !== false)){
echo'kitchen lights on<br>';
}
if ((strpos($output[$i],'lights') !== false)&&(strpos($output[$i],'off') !== false)&&(strpos($output[$i],'kitchen') !== false)){
echo'kitchen lights off<br>';
}
if ((strpos($output[$i],'lights') !== false)&&(strpos($output[$i],'on') !== false)&&(strpos($output[$i],'living') !== false)){
echo'living lights on<br>';
}
if ((strpos($output[$i],'lights') !== false)&&(strpos($output[$i],'off') !== false)&&(strpos($output[$i],'living') !== false)){
echo'living lights off<br>';
}
if ((strpos($output[$i],'lights') !== false)&&(strpos($output[$i],'on') !== false)&&(strpos($output[$i],'bed') !== false)){
echo'bed lights on<br>';
}
if ((strpos($output[$i],'lights') !== false)&&(strpos($output[$i],'off') !== false)&&(strpos($output[$i],'bed') !== false)){
echo'bed lights off<br>';
}   
$i++;
}
$input='厨房灯打开,床和生活灯关闭';
$output=preg_split(“/(and)/”,$input);
$num=(int)计数($output);
$i=0;
而我

解析自然语言并非易事,如果您想要一个真正的自然语言解析器,我建议您尝试使用现有的项目或库

话虽如此,如果你愿意限制语法和所涉及的关键字,你也许可以简化它。首先你需要知道什么是重要的——你在“地方”(卧室、厨房)有“东西”(灯、风扇),需要进入特定状态(“开”、“关”)

我会将字符串放入一个单词数组,要么使用,要么在
'
上分解

现在你有了一个单词数组,从末尾开始,向后寻找一个“状态”——开或关。然后按照这个数组向后寻找一个“东西”,最后是一个“地方”。如果你到达另一个状态,那么你可以重新开始

让我尝试用伪代码来实现这一点:

// array of words is inArray
currentPlace = null;
currentThing = null; 
currentState = null;
for (i = (inArray.length - 1); i >= 0; i--) {
    word = inArray[i];

    if (isState(word)) {

      currentState = word;
      currentPlace = null;
      currentThing = null;

    } else if (currentState) {

        if (isThing(word)) { 

             currentThing = word;
             currentPlace = null;

        } else if (currentThing) { 

             if (isPlace(word)) { 
                 currentPlace = word
                 // Apply currentState to currentThing in currentPlace
             }
             // skip non-place, thing or state word. 
        }
        // Skip when we don't have a thing to go with our state

    } 
    // Skip when we don't have a current state and we haven't found a state
}
写了这些之后,很明显,它应该使用状态机和switch语句——这表明我应该首先在纸上设计它。如果你变得更复杂,你想使用状态机来实现逻辑——状态将是“lookingForState”、“lookingForThing”等

另外,您实际上不需要将
currentPlace
作为变量,但我将保留它,因为它使逻辑更加清晰

编辑

如果你想支持“打开卧室的灯”,你需要调整逻辑(如果你没有东西,你需要保存“位置”)。如果你还想支持“打开卧室的灯”,你需要更进一步

想一想,我想知道你是否能做到:

have a currentState variable and arrays for currentPlace and currentThing
for each word 
    if it's a state:
        store it in currentState 
    if it's a thing, or place:
        add it to the approriate array
        if currentState is set and there is content in currentPlaces and currentThings:
            apply currentState to all currentThings in all currentPlaces
这还不太清楚,但其中一个实现可能会为您提供一个起点

编辑2

好的,我已经测试过了,但是由于英语的结构,出现了一些问题。问题是如果你想支持“开启…”和“开启…”,那么你需要使用我的第二个伪代码,但这并不容易,因为句子中有“和”。例如:

打开厨房的灯,关掉卧室和客厅的灯

第一个and连接两个语句,第二个and连接到位置。正确的方法是找出什么适用于什么

有两个快速选项,首先您可以坚持使用不同的单词或短语连接两个命令:

打开厨房灯,然后关闭卧室和客厅灯。 打开厨房的灯,同时关闭卧室和客厅的灯

或者,这可能更容易,您可以坚持只使用“关闭…关闭/打开”形式的命令。这适用于我上面的第一个psuedocode

第一个伪代码

请注意,如果有可能出现标点符号等,您可能需要对字符串进行大量预处理。您可能还需要考虑替换“客厅”(以及类似的两个单词短语)使用“livingroom”而不是像我这样只匹配一个单词并希望达到最佳效果。此外,代码可以简化一点,但我想让它接近psuedocode示例

编辑3

这会处理一些额外的句子,并且会得到更好的清理,它仍然依赖于每个子句末尾的“状态”,因为这是它用来作为应用操作的触发器(此版本可能会向前读取,而不是向后读取)。此外,它不会处理以下内容:

Turn my kitchen fan and my bedroom lights on and living room lights off.
你必须做一些更复杂的事情来理解“厨房”和“风扇”以及“卧室”和“灯光”之间的关系


只要输入/说出命令的人遵循一些基本规则,这些技术的一些组合可能足以完成一些令人印象深刻的任务。

这当然不是最有效的解决方案,但这里有一个。你肯定可以改进它,比如缓存正则表达式,但你明白了。最后一项是每个子数组都是操作

var s = 'Turn my kitchen lights on and my bedroom lights on and living room lights off and my test and another test off',
    r = s.replace(/^Turn|\s*my/g, '').match(/.+? (on|off)/g).map(function(item) {
        var items = item.trim().replace(/^and\s*/, '').split(/\s*and\s*/),
            last = items.pop().split(' '),
            op = last.pop();
        return items.concat([last.join(' '), op]);
    });

console.log(r);
介意解释一下你使用的逻辑吗…我的意思是我在读代码,但我 我只是好奇你是否能说得更好

逻辑其实很简单,也许太简单了:

var s = 'Turn my kitchen lights on and my bedroom lights on and living room lights off and my test and another test off',
    r = s
        .replace(/^Turn|\s*my/g, '') //remove noisy words
        .match(/.+? (on|off)/g) //capture all groups of [some things][on|off]
        //for each of those groups, generate a new array from the returned results
        .map(function(item) {
            var items = item.trim()
                    .replace(/^and\s*/, '') //remove and[space] at the beginning of string
                    //split on and to get all things, for instance if we have
                    //test and another test off, we want ['test', 'another test off']
                    .split(/\s*and\s*/),
                //split the last item on spaces, with previous example we would get
                //['another', 'test', 'off']
                last = items.pop().split(' '),
                op = last.pop(); //on/off will always be the last item in the array, pop it
            //items now contains ['test'], concatenate with the array passed as argument
            return items.concat(
                [
                    //last is ['another', 'test'], rejoin it together to give 'another test'
                    last.join(' '),
                    op //this is the operation
                ]
            );
        });
编辑:在我发布答案的时候,我还没有意识到你需要多么复杂和灵活
var s = 'Turn my kitchen lights on and my bedroom lights on and living room lights off and my test and another test off',
    r = s
        .replace(/^Turn|\s*my/g, '') //remove noisy words
        .match(/.+? (on|off)/g) //capture all groups of [some things][on|off]
        //for each of those groups, generate a new array from the returned results
        .map(function(item) {
            var items = item.trim()
                    .replace(/^and\s*/, '') //remove and[space] at the beginning of string
                    //split on and to get all things, for instance if we have
                    //test and another test off, we want ['test', 'another test off']
                    .split(/\s*and\s*/),
                //split the last item on spaces, with previous example we would get
                //['another', 'test', 'off']
                last = items.pop().split(' '),
                op = last.pop(); //on/off will always be the last item in the array, pop it
            //items now contains ['test'], concatenate with the array passed as argument
            return items.concat(
                [
                    //last is ['another', 'test'], rejoin it together to give 'another test'
                    last.join(' '),
                    op //this is the operation
                ]
            );
        });