За последние 24 часа нас посетили 22259 программистов и 1082 робота. Сейчас ищут 667 программистов ...

Разбор слова на части

Тема в разделе "PHP для новичков", создана пользователем Yankovitz, 12 мар 2019.

  1. Yankovitz

    Yankovitz Активный пользователь

    С нами с:
    21 ноя 2014
    Сообщения:
    194
    Симпатии:
    6
    Добрый день. Пытаюсь сделать морфологический разбор слов (не русский язык). Дело такое, язык - агглютинативный, что немного упрощает задачу. Агглютинация - явление, когда словообразование происходит за счёт прибавления окончания. При этом действует правило: одна морфема - одно окончание.

    Как я понимаю, из-за особенностей языка, разбор нужно проводить с конца, проверяя совпадение окончания одно за другим.

    Условно, есть набор окончаний:
    PHP:
    1. $affiks = array(
    2.     '0'  => array( 'mı', 'mi', 'mu', 'mü' ),
    3.     '1'  => array( 'im', 'ım' ),
    4.     '2' => array( 'lar', 'lər' ),
    5. );
    Есть слово: akraba-lar-ım
    Нужно разобрать слово, чтобы получилось следующее:
    akraba - основа
    lar - индекс 2
    im - индекс 1
    Делаю так:
    PHP:
    1. foreach ( $affiks as $id => $ends ) {
    2.     foreach ( $ends as $end ) {
    3.         if ( preg_match( '/' . $end . '$/', 'akrabalarım' ) ) {
    4.             $result .= 'Найдено окончание: ' . $end . ' с ID: ' . $id;
    5.         }
    6.     }
    7. }
    Однако выводит пока "Найдено окончание: ım с ID: 1"
    Буду благодарен за любую помощь.
     
  2. Maputo

    Maputo Активный пользователь

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Есть быстрый вариант (по количеству правок кода), но тут важен порядок окончаний (расположение в массиве):
    PHP:
    1. $word = 'akrabalarım';
    2. foreach ( $affiks as $id => $ends ) {
    3.     foreach ( $ends as $end ) {
    4.         if ( preg_match( '/^(.+)' . $end . '$/', $word, $m) ) {
    5.             $result .= 'Найдено окончание: ' . $end . ' с ID: ' . $id;
    6.             $word = $m[1];
    7.         }
    8.     }
    9. }
    10. $result .= 'Основа: ' . $word;
    В слове akrabaımlar найдет только lar
     
  3. Artur_hopf

    Artur_hopf Активный пользователь

    С нами с:
    7 май 2018
    Сообщения:
    2.266
    Симпатии:
    405
    @Yankovitz хз может можно проще:
    PHP:
    1. affiks = array(
    2.     '0'  => array( 'mı', 'mi', 'mu', 'mü' ),
    3.     '1'  => array( 'im', 'ım' ),
    4.     '2' => array( 'lar', 'lər' ),
    5.   );
    6.  
    7.   $name = 'akrabalarim';
    8.   $result = '';
    9.   $base = '';
    10.   $parse = parse($affiks, $name, $result, $base);
    11.   echo $parse;
    12.  
    13.   function parse($arr, $str, $result, $base){
    14.     foreach($arr as $id => $ends){
    15.       foreach ($ends as $end) {
    16.         $pos = strripos($str, $end);
    17.         if ($pos !== false) {
    18.           $result .= 'Найдено окончание: '.$end.' с ID: '.$id.'<br>';
    19.           $base = substr($str, 0, $pos);
    20.           parse($arr, $base, $result, $base);
    21.         }
    22.       }
    23.     }
    24.     $result .= $base.' - основа';
    25.     return $result;
    26.   }
     
  4. Artur_hopf

    Artur_hopf Активный пользователь

    С нами с:
    7 май 2018
    Сообщения:
    2.266
    Симпатии:
    405
    Только текст надо задом наперед читать =)
     
  5. Artur_hopf

    Artur_hopf Активный пользователь

    С нами с:
    7 май 2018
    Сообщения:
    2.266
    Симпатии:
    405
    Не то в общем, потестил. Нужен кто то поумнее =)
     
  6. Valick

    Valick Активный пользователь

    С нами с:
    12 авг 2018
    Сообщения:
    1.911
    Симпатии:
    328
  7. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.155
    Симпатии:
    1.769
    Адрес:
    :сердА
    ВЫЗЫВАЙТЕ РУМИЛАНА!
    @johovich , приди!!!

    Он на этой хрени собаку съел и даже расширение для пыха написал, которое под такое дело годится.
     
  8. Ivan Vanko

    Ivan Vanko Новичок

    С нами с:
    3 мар 2019
    Сообщения:
    20
    Симпатии:
    2
    У меня такой вариант )

    PHP:
    1. $affiks = array(
    2.   '0'  => array( 'mı', 'mi', 'mu', 'mü' ),
    3.   '1'  => array( 'im', 'ım' ),
    4.   '2' => array( 'lar', 'lər' ),
    5. );
    6.  
    7. $word = 'akrabalarım';
    8. $word_parts = [];
    9.  
    10. function disassemble ($affiks, $word) {
    11.    global $word_parts;
    12.  
    13.    foreach ($affiks as $end_group => $ends) {
    14.    
    15.      foreach ($ends as $end) {
    16.      
    17.        $reg_exp = '/(' . $end . '){1}$/';
    18.        $result = preg_replace($reg_exp, '', $word);
    19.      
    20.        // Если преобразование по регулярке произошло, то записываем в массив $word_parts индекс группы аффиксов и выходим обоих циклов. Группу аффиксов удаляем из массива, чтобы не проверять повторно.
    21.      
    22.        if ($result !== $word) {
    23.      
    24.          //echo "ID: " . $end_group . " " . $end . " " . $result . "<br>";
    25.          $word_parts[] = [$end, $end_group];
    26.          unset ($affiks[$end_group]);
    27.          break 2;
    28.        }
    29.      }
    30.    }
    31.  
    32.    return ($result === $word) ? [$result, 'основа'] : (disassemble($affiks, $result));  
    33. }
    34.  
    35. $word_parts[] = disassemble($affiks, $word);
    36.  
    37. var_dump($word_parts);

    На выходе:

    Код (Text):
    1.  
    2.  
    3. array(3) {
    4.   [0]=>
    5.   array(2) {
    6.     [0]=>
    7.     string(3) "ım"
    8.     [1]=>
    9.     int(1)
    10.   }
    11.   [1]=>
    12.   array(2) {
    13.     [0]=>
    14.     string(3) "lar"
    15.     [1]=>
    16.     int(2)
    17.   }
    18.   [2]=>
    19.   array(2) {
    20.     [0]=>
    21.     string(6) "akraba"
    22.     [1]=>
    23.     string(12) "основа"
    24.   }
    25. }
     
    #8 Ivan Vanko, 13 мар 2019
    Последнее редактирование: 13 мар 2019
    Yankovitz нравится это.
  9. Valick

    Valick Активный пользователь

    С нами с:
    12 авг 2018
    Сообщения:
    1.911
    Симпатии:
    328
  10. Ivan Vanko

    Ivan Vanko Новичок

    С нами с:
    3 мар 2019
    Сообщения:
    20
    Симпатии:
    2
    Тут, мне кажется, надо в тонкости языка уже вникать. Сколько там может быть этих окончаний? Может ли окончание одного типа быть и в самом хвосте слова, и заодно еще перед каким нибудь суффиксом. Думаю, там максимум цепочка из 3х окончаний (суффиксов, аффиксов) будет. И по идее они должны быть уникальные в одном слове. Поэтому использованную в одном цикле группу аффиксов я предложил больше не рассматривать в последующих циклах.
     
  11. Maputo

    Maputo Активный пользователь

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    По-моему автор забил на топик.
    А так все окончания можно запихнуть в одну регулярку. Все зависит от того
     
  12. Valick

    Valick Активный пользователь

    С нами с:
    12 авг 2018
    Сообщения:
    1.911
    Симпатии:
    328
  13. Yankovitz

    Yankovitz Активный пользователь

    С нами с:
    21 ноя 2014
    Сообщения:
    194
    Симпатии:
    6
    Это верное замечание. В слове не может быть одновременно нескольких вариаций одного окончания:
    akrabalarım - да,
    akraba-lar-ım-ım, akraba-lar-lar-ım - нет
    То есть, в исходно массиве:
    Код (Text):
    1. $affiks = array(
    2.     '0'  => array( 'mı', 'mi', 'mu', 'mü' ),
    3.     '1'  => array( 'im', 'ım' ),
    4.     '2' => array( 'lar', 'lər' ),
    5.     '2' => array( 'da', 'də' ),
    6. );
    Один элемент массива - одно окончание с несколькими вариациями. По сути, количество окончаний может быть теоретически не ограничено, - сколько есть. Например: akraba-lar-ım-da-mı
    --- Добавлено ---
    Простите, я просто немого закрутился. Возвращаюсь в тему
     
  14. Valick

    Valick Активный пользователь

    С нами с:
    12 авг 2018
    Сообщения:
    1.911
    Симпатии:
    328
    @Yankovitz, т.е. надо вывести ошибку если окончания повторяются? В любом случае ссылка есть, можете любые слова пробовать.
     
  15. Yankovitz

    Yankovitz Активный пользователь

    С нами с:
    21 ноя 2014
    Сообщения:
    194
    Симпатии:
    6
    И ещё один момент. Проверку нужно делать с окончаний в последовательности с наиболее длинных к менее длинным. Так как есть помимо окончаний array( 'lar', 'lər' ) есть ещё array( 'ar', 'ər' ),
    Это чтобы такое разделение не происходило: akrabal-ar-ım (что не верно)
    --- Добавлено ---
    У меня почему-то не работает ссылка
     
  16. Valick

    Valick Активный пользователь

    С нами с:
    12 авг 2018
    Сообщения:
    1.911
    Симпатии:
    328
    полный перечень окончаний есть?
     
  17. Yankovitz

    Yankovitz Активный пользователь

    С нами с:
    21 ноя 2014
    Сообщения:
    194
    Симпатии:
    6
    Есть:
    Код (Text):
    1.         $affiks = array(
    2.             '0'  => array( 'na', 'nı', 'a', 'ı' ),
    3.             '1'  => array( 'acaq', 'əcək' ),
    4.             '2'  => array( 'am', 'əm' ),
    5.             '3'  => array( 'an', 'ən' ),
    6.             '4'  => array( 'ca', 'cə' ),
    7.             '5'  => array( 'casına', 'cəsinə' ),
    8.             '6'  => array( 'mı', 'mi', 'mu', 'mü' ),
    9.             '7'  => array( 'çı', 'çi', 'çu', 'çü' ),
    10.             '8'  => array( 'da', 'də' ),
    11.             '9'  => array( 'dan', 'dən' ),
    12.             '10' => array( 'lar', 'lər' ),
    13.             '11' => array( 'daş' ),
    14.             '12' => array( 'dı', 'di', 'du', 'dü' ),
    15.             '13' => array( 'dığı', 'diyi', 'duğu', 'düğü' ),
    16.             '14' => array( 'dır', 'dir', 'dur', 'dür' ),
    17.             '15' => array( 'dırlar', 'dirlər', 'durlar', 'dürlər' ),
    18.             '16' => array( 'sı', 'si', 'su', 'sü', 'ı', 'i', 'u', 'ü' ),
    19.             '17' => array( '(n)ı', '(n)i', '(n)u', '(n)ü' ),
    20.             '18' => array( 'ıb', 'ib', 'ub', 'üb' ),
    21.             '19' => array( 'ıq', 'ik', 'uq', 'ük' ),
    22.             '20' => array( 'ım', 'im', 'um', 'üm' ),
    23.             '21' => array( 'ın', 'in', 'un', 'ün' ),
    24.             '22' => array( 'nın', 'nin', 'nun', 'nün', 'ın', 'in', 'un', 'ün' ),
    25.             '23' => array( '(y)ın', '(y)in', '(y)un', '(y)ün' ),
    26.             '24' => array( 'ımız', 'imiz', 'umuz', 'ümüz' ),
    27.             '25' => array( 'ınız', 'iniz', 'unuz', 'ünüz' ),
    28.             '26' => array( 'yır', 'yir', 'yur', 'yür', 'ır', 'ir', 'ur', 'ür' ),
    29.             '27' => array( 'yırdı', 'yirdi', 'yurdu', 'yürdü', 'ırdı', 'irdi', 'urdu', 'ürdü' ),
    30.             '28' => array( 'lı', 'li', 'lu', 'lü' ),
    31.             '29' => array( 'lıq', 'lik', 'luq', 'lük' ),
    32.             '30' => array( 'maq', 'mək' ),
    33.             '31' => array( 'malı', 'məli' ),
    34.             '32' => array( 'mı', 'mi', 'mu', 'mü' ),
    35.             '33' => array( 'mışdı', 'mişdi', 'muşdu', 'müşdü' ),
    36.             '34' => array( 'saydı', 'səydi' ),
    37.             '35' => array( 'sa', 'sə' ),
    38.             '36' => array( 'sən', 'san' ),
    39.             '37' => array( 'sınız', 'siniz', 'sunuz', 'sünüz' ),
    40.             '38' => array( 'sız', 'siz', 'suz', 'süz' ),
    41.             '39' => array( 'sın', 'sin', 'sun', 'sün' ),
    42.             '40' => array( 'n' ),
    43.             '41' => array( 'm' ),
    44.         );
     
  18. Maputo

    Maputo Активный пользователь

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    @Yankovitz, не пробовали обратный массив? Ключи - окончания, а идентификаторы - значения.
    --- Добавлено ---
    И что вот тут значат скобки?
    PHP:
    1.  '23' => array( '(y)ın', '(y)in', '(y)un', '(y)ün' ),
    Это символы, которые используются в слове?
     
  19. Ivan Vanko

    Ivan Vanko Новичок

    С нами с:
    3 мар 2019
    Сообщения:
    20
    Симпатии:
    2
    Можно пробежаться по всему массиву этих окончаний, проверить каждое из них на вхождение его в состав других окончаний (в конце), составить статистику и по ней отсортировать группы окончаний и окончания в каждой группе. И оно само выйдет так, что первыми окажутся группы с самыми длинными окончаниями.
    Примитивно, конечно, но можно попробовать.
     
  20. Valick

    Valick Активный пользователь

    С нами с:
    12 авг 2018
    Сообщения:
    1.911
    Симпатии:
    328
    @Ivan Vanko, не нужно. @Maputo дал подсказку, массив надо обратный и одномерный. Думай в этом направлении.
     
  21. Ivan Vanko

    Ivan Vanko Новичок

    С нами с:
    3 мар 2019
    Сообщения:
    20
    Симпатии:
    2
    Да, тоже пришел к такому выводу. Сортировка всех окончаний по принципу имен в файловой системе, только их все предварительно надо развернуть (отразить). За каждым закрепить индекс группы. И исключать эти окончания из слов в том порядке, в каком они естественным образом окажутся
     
  22. Yankovitz

    Yankovitz Активный пользователь

    С нами с:
    21 ноя 2014
    Сообщения:
    194
    Симпатии:
    6
    Хорошая мысль. Попробую так сделать