анализировать естественный язык

Для начала: я знаю, что эта система будет иметь недостатки!

ПРИМЕЧАНИЕ: Я добавляю несколько других языков, потому что я не нахожу эту проблему специфичной для php..A 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++; } 

Проверка кода 2: Примечание. Это относится ко всем приведенным выше примерам!

 <?php //works list $inp[]='turn the lights in the bedroom on'; $inp[]='Turn on the bedroom light'; $inp[]='turn on the lights in the bedroom'; $inp[]='Turn my kitchen and my bedroom and living room lights off.'; $inp[]='Turn the light in the kitchen on and the fan in the bedroom off'; $inp[]='Turn my kitchen lights on and my bedroom and living room lights off'; $inp[]='Turn my kitchen fan and my bedroom lights on and living room lights off.'; $inp[]='Turn my kitchen lights on and my bedroom lights on and living room lights off'; $inp[] = 'kitchen lights on and bath and living lights off'; $inp[] = 'flip on the lights in the living room'; $inp[] = 'turn on all lights'; //does not work list //$inp[] = 'turn on all lights but living'; foreach ($inp as $input){ $input = trim($input); $input = rtrim($input, '.'); $input = trim($input); $input = rtrim($input, '.'); $words = explode(" ", $input); $state = array('and','but','on','off','all','living','bed','bedroom','bath','kitchen','dining','light','lights','fan','tv'); $result = array_intersect($words, $state); $result = implode(" ", $result); $result = trim($result); //$result = preg_split('/(and|but)/',$input,-1, PREG_SPLIT_DELIM_CAPTURE); $result = preg_split( "/ (and|but) /", $result ); //$result = explode("and", $result); $sep=array(); foreach($result as $string){ $word = explode(" ", $string); $sep[]=$word; } $test=array(); $num = (int)count($sep); $i=0; while($i<($num)){ $result = (int)count(array_intersect($sep[$i], $state)); $j=$i; while($result<=3) { $imp = implode(" ", $sep[$j]); if(isset($test[$i])){$test[$i]=$imp.' '.$test[$i];} else{$test[$i]=$imp;} if ($result>=3){$j++;break;} $result = (int)count(array_intersect($sep[++$j], $state)); } $i=$j; } print_r($test); echo '<br>'; } ?> в <?php //works list $inp[]='turn the lights in the bedroom on'; $inp[]='Turn on the bedroom light'; $inp[]='turn on the lights in the bedroom'; $inp[]='Turn my kitchen and my bedroom and living room lights off.'; $inp[]='Turn the light in the kitchen on and the fan in the bedroom off'; $inp[]='Turn my kitchen lights on and my bedroom and living room lights off'; $inp[]='Turn my kitchen fan and my bedroom lights on and living room lights off.'; $inp[]='Turn my kitchen lights on and my bedroom lights on and living room lights off'; $inp[] = 'kitchen lights on and bath and living lights off'; $inp[] = 'flip on the lights in the living room'; $inp[] = 'turn on all lights'; //does not work list //$inp[] = 'turn on all lights but living'; foreach ($inp as $input){ $input = trim($input); $input = rtrim($input, '.'); $input = trim($input); $input = rtrim($input, '.'); $words = explode(" ", $input); $state = array('and','but','on','off','all','living','bed','bedroom','bath','kitchen','dining','light','lights','fan','tv'); $result = array_intersect($words, $state); $result = implode(" ", $result); $result = trim($result); //$result = preg_split('/(and|but)/',$input,-1, PREG_SPLIT_DELIM_CAPTURE); $result = preg_split( "/ (and|but) /", $result ); //$result = explode("and", $result); $sep=array(); foreach($result as $string){ $word = explode(" ", $string); $sep[]=$word; } $test=array(); $num = (int)count($sep); $i=0; while($i<($num)){ $result = (int)count(array_intersect($sep[$i], $state)); $j=$i; while($result<=3) { $imp = implode(" ", $sep[$j]); if(isset($test[$i])){$test[$i]=$imp.' '.$test[$i];} else{$test[$i]=$imp;} if ($result>=3){$j++;break;} $result = (int)count(array_intersect($sep[++$j], $state)); } $i=$j; } print_r($test); echo '<br>'; } ?> в <?php //works list $inp[]='turn the lights in the bedroom on'; $inp[]='Turn on the bedroom light'; $inp[]='turn on the lights in the bedroom'; $inp[]='Turn my kitchen and my bedroom and living room lights off.'; $inp[]='Turn the light in the kitchen on and the fan in the bedroom off'; $inp[]='Turn my kitchen lights on and my bedroom and living room lights off'; $inp[]='Turn my kitchen fan and my bedroom lights on and living room lights off.'; $inp[]='Turn my kitchen lights on and my bedroom lights on and living room lights off'; $inp[] = 'kitchen lights on and bath and living lights off'; $inp[] = 'flip on the lights in the living room'; $inp[] = 'turn on all lights'; //does not work list //$inp[] = 'turn on all lights but living'; foreach ($inp as $input){ $input = trim($input); $input = rtrim($input, '.'); $input = trim($input); $input = rtrim($input, '.'); $words = explode(" ", $input); $state = array('and','but','on','off','all','living','bed','bedroom','bath','kitchen','dining','light','lights','fan','tv'); $result = array_intersect($words, $state); $result = implode(" ", $result); $result = trim($result); //$result = preg_split('/(and|but)/',$input,-1, PREG_SPLIT_DELIM_CAPTURE); $result = preg_split( "/ (and|but) /", $result ); //$result = explode("and", $result); $sep=array(); foreach($result as $string){ $word = explode(" ", $string); $sep[]=$word; } $test=array(); $num = (int)count($sep); $i=0; while($i<($num)){ $result = (int)count(array_intersect($sep[$i], $state)); $j=$i; while($result<=3) { $imp = implode(" ", $sep[$j]); if(isset($test[$i])){$test[$i]=$imp.' '.$test[$i];} else{$test[$i]=$imp;} if ($result>=3){$j++;break;} $result = (int)count(array_intersect($sep[++$j], $state)); } $i=$j; } print_r($test); echo '<br>'; } ?> 

Анализ естественного языка является нетривиальным, если вы хотите использовать истинный парсер для естественного языка, я бы рекомендовал вам попробовать использовать существующий проект или библиотеку. Вот веб-парсер , основанный на Стэнфордском Парсере . Или википедия – хороший прыжок.

Сказав это, если вы хотите ограничить синтаксис и ключевые слова, вы можете упростить его. Сначала вам нужно знать, что важно – у вас есть «вещи» (фары, вентиляторы) в «местах» (спальня, кухня), которые должны войти в определенное состояние («включено», «выключено»).

Я бы получил строку в массив слов, используя str_tok , или просто взорвался на ' ' .

Теперь у вас есть набор слов, начинающихся в конце и возвращающихся назад, для поиска «состояния» – вкл. Или выкл. Затем следуйте за этим назад, ища «вещь», и, наконец, «место». Если вы нажмете другое состояние, вы можете начать заново.

Позвольте мне попробовать и сделать это в псевдокоде:

 // 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 } 

И, написав это, довольно ясно, что он должен был использовать конечный автомат и операторы переключения, которые показывают, что я должен был сначала создать его на бумаге. Если вы становитесь более сложным, вы хотите использовать конечный автомат для реализации логических состояний: «lookForState», «lookForThing» и т. Д.

Также вам не нужен 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 

Это не совсем так, но одна из этих реализаций может дать вам отправную точку.

EDIT 2

Хорошо, я проверил это, и есть несколько проблем из-за того, как структурирован английский. Проблема в том, что если вы хотите поддерживать «Включить …» и «Включить … дальше», вам нужно использовать мой второй псевдокод, но это не сработает из-за «и в предложении». Например:

Включите свет моей кухни, и моя спальня и гостиная погаснет.

Первый и объединяет два утверждения, второе и присоединяется к местам. Правильный способ сделать это – нарисовать диаграмму предложения, чтобы выяснить, что к чему.

Есть два быстрых варианта: сначала вы можете настаивать на использовании другого слова или фразы для объединения двух команд:

Поверните мои огни кухни, после чего моя спальня и гостиная погаснет. Включите свет моей кухни, а также погаснет спальня и гостиная.

В качестве альтернативы, и это, вероятно, проще, вы можете настаивать только на наличии команд формы «Turn … off / on». Это работает с моим первым psuedocode выше.

JavaScript Пример первого psuedocode.

Обратите внимание: вам, вероятно, придется предварительно обработать строку, если есть вероятность пунктуации и т. Д. Вы также можете посмотреть на замену «гостиной» (и аналогичных двух словосочетаний) на «гостиную», а не просто совпадение одно слово и надеясь на лучшее, что я делаю. Кроме того, код может быть немного упрощен, но я хотел бы держать его близко к примеру psuedocode.

ИЗМЕНИТЬ 3

Новый пример Javascript

Это обрабатывает некоторые дополнительные предложения и очищается немного лучше, он все еще полагается на «состояние», выходящее в конце каждого предложения, так как это то, что он использует в качестве триггера для применения этих действий (эта версия, вероятно, могла бы читать вперед, а не назад ). Кроме того, он не будет обрабатывать что-то вроде:

 Turn my kitchen fan and my bedroom lights on and living room lights off. 

Вам нужно сделать что-то более сложное, чтобы понять взаимосвязь между «кухней» и «веером», «спальней» и «огнями».

Некоторая комбинация этих методов, вероятно, достаточно для того, чтобы сделать что-то довольно впечатляющее, если кто-либо, входящий / выступающий с командами, следует некоторым основным правилам.

Это, безусловно, не самое эффективное решение, но вот одно. Вы можете определенно улучшить его, например, кешировать регулярные выражения, но вы получите эту идею. Последний элемент в каждом под-массиве – это операция.

DEMO

 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 ] ); }); 

EDIT: В то время, когда я отправил ответ, я не понял, насколько сложным и гибким вам было нужно это. Решение, которое я предоставил, будет работать только для предложений, структурированных, как в моем примере, с идентифицируемыми шумными словами и конкретным порядком команд. Для чего-то более сложного, у вас не будет другого выбора, кроме как создать парсер, например, @SpaceDog. Я попытаюсь что-то придумать, как только у меня будет достаточно времени.

Я работаю над разбором меню и рецептов (не закончено), и это мой подход:

  • найти разделители предложений (я использую AND, а также другие)
  • проанализируйте каждое предложение, чтобы найти key слова, которые вам нужны (свет / лампочки / etc .., on / off)
  • если у вас ограниченный набор мест (кухня, ванная комната и т. д.)
    • поиск этих ключевых слов, удаление других
    • ELSE
    • удалите extra words которые могут использовать некоторые люди (яркие, красочные и т. д.)
  • сохраните его в массив, что-то, что может выглядеть так:
    • какие
    • где
  • если у вас нет одного поля, оставьте его пустым
  • для каждого результата проверьте, что у вас есть, и если у вас пустое поле, заполните его предыдущим разбором

IE: включите свет в спальне и на кухне

  • 1:
    • включите свет в спальне
    • что: светится
    • где: спальня
  • 2:
    • на кухне
    • какие:
    • где: кухня

what_2 пуст, тогда what_2 lights on

имейте в виду, что когда-то нужно заполнить массив следующими результатами (в зависимости от того, как структурировано предложение, но это редко), я добавляю «+» или «-», чтобы я знал, должен ли я идти вперед или назад, чтобы найти недостающие части во время разбора