PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » PureBasic для Windows » как автоматически удалить окончание в словах


как автоматически удалить окончание в словах

Сообщений 1 страница 19 из 19

1

Есть список слов, всего 11 тыс. Они получены методом поиска всех слов в справке, и я хочу составить индекс.
Для пример несколько слов из списка:

Код:
аккумулятор
аккумулятора
аккумуляторе
аккумуляторная
аккумуляторную
аккумуляторов
аккумулятору
аккумуляторы

Мне нужно либо получить одно слово "аккумулятор", либо тот же список с удалёнными окончаниями, при этом я обработаю список удалением дубликатов и получу слово в одном экземпляре.
Задача из 11 тыс. слов сделать список размером 1000 слов за счёт удаления однокоренных с окончаниями. Я хочу использовать это слово для поиска, то есть человек вводит в строке поиска "акк" и ему предлагается в раскрывающемся списке автозавершения слово "аккумулятор". Предлагаемых слов может быть больше, например для "то" выводится например "токовый", "токарь" и т.д. Просто я не хочу чтобы получить десяток однокоренных слов с разными окончаниями, по сути забивающие список мусором.
Возможно нужен какой-то словарь, который вычленить корни слов, то есть умеет у слова показать корень и я смогу получить корни слов, в общем любые идеи...

0

2

AZJIO
Обрезать не проблема. Я бы на PowerShell скрипт написал.
Алгоритм примерно такой (для отсортированного списка, когда одинаково начинающиеся более короткие выше более длинных):
- Берём первую строку в качестве базового слова, сохраняем её.
- Цикл по всем следующим строкам:
- Если строка длиннее базового слова и начало совпадает с базовым словом, пропускаем строку
- Иначе сохраняем строку и берём её в качестве базового слова.
Хотя, мне кажется, подобный "индекс" будет неправильным. Ведь аккумулятор и аккумуляторная (помещение для аккумуляторов) это всё-таки разные слова, а не просто словоформы одного слова.
Другие подобные примеры: компьютер и компьютеризация, снег и снегоход, стол и столяр.
В то же время, в словах аккумуляторная и аккумуляторную так просто не обрезать окончание.
Плюс 100500 нюансов русского языка в виде беглых гласных, чередований гласных...
Мне кажется, нужен словарь, по возможности наиболее полный, содержащий только базовые формы слов. Вот по нему и проверять.

Отредактировано Smitis (07.02.2024 15:46:37)

0

3

Smitis
У меня возникала мысль, если 2 и более слов подряд имеют одинаковость 70 и более процентов, то обрезать до этих 70℅. Не однокоренные слова будут иметь границу 50℅. Тот же стол и столяр 4+2, это будет 66+33℅. В любом случае можно под регулировать проценты.
Если я потеряю 10℅ слов в любом случае это лучше чем руками перебирать 11 тыс.

0

4

А ещё вам следует предусмотреть что пользователь может вводить слова с ошибками или опечатками.

0

5

AZJIO написал(а):

Тот же стол и столяр 4+2, это будет 66+33℅.

столов, столам (мн.ч.)
столик
столиц (от столицы)
столб
столен (не знаю, что это)
столищ
столки
столку
столок
столп
столпи
столпя
столь
Это я по словарю пробежался )) https://dikmax.name/post/russian-dictionary/
В общем, я бы копал в этом направлении - словарь со словоформами. К сожалению, по ссылке выше словоформы от основных слов отделены, какое к чему относится неизвестно.

0

6

В общем спасибо за подсказку destiny child здесь и всем кто пытался помочь. Онлайн Стемминг просто выдаёт готовый результат.

0

7

Привет! На самом деле интересный вопрос. Был заинтересован сегодня твоей задачей. Думал об организации данных, в течении дня.
И есть идея, чтобы хранить в хэш-карте, создав структуру посимвольной организации.
Примерно так:
https://forumupload.ru/uploads/0009/ae/28/644/t599432.png
Условия здесь можно задать более-мене свободно.
И даже если грузить всю базу слов, это хорошо сжимает данные, потому-что не нужно хранить все слова, а только варианты букв.
Пока только алгоритм добавления. Но стабильный:

Код:
;- Structure
Structure CharDB
  PrevChar$           ; прошлая буква нужна, для правильного определения структуры слова в рекурсии, по сути это только один символ
  ThisIsCharEndWord.a ; поисковику, это поможет понять, что на этой букве слово завершено и его можно отправлять в вывдачу. Один байт да/нет
  Map Child.CharDB()  ; следующие буквы
EndStructure

;- Variable
Global NewMap CharDB.CharDB()

; добавление в базу
Procedure Add( Word$ )
  Protected i, *Parent.CharDB = 0, PrevChar$ = ""
  For i = 1 To Len( Word$ )
    Protected Char$ = LCase( Mid( Word$, i, 1 ) )
    If Not *Parent ; это только для первых букв
      If FindMapElement( CharDB(), Char$ )
        *Parent = CharDB()
      Else
        *Parent = AddMapElement( CharDB(), Char$ )
      EndIf
    Else ; здесь продолжается добавление всех следующих букв, после первой
      If FindMapElement( *Parent\Child(), Char$ )
        *Parent = *Parent\Child()
      Else
        AddMapElement( *Parent\Child(), Char$ )
        *Parent\Child()\PrevChar$ = PrevChar$ ; текущая буква, будет знать о предыдущей
        If i >= Len(Word$)
          *Parent\Child()\ThisIsCharEndWord = #True ; указываем, что это конечный символ в слове
        EndIf
        *Parent = *Parent\Child()
      EndIf
    EndIf
    PrevChar$ = Char$
  Next
EndProcedure  

; - добавление
Add("Лазер")
Add("Лазера")
Add("Лазерам")
Add("Лазерами")
Add("Лазерах")
Add("Лазере")
Add("Лазеров")
Add("Лазером")
Add("Лазеру")
Add("Лазеры")

Алгоритмы поиска и извлечения ещё в тестах, да и времени на это больше надо. Чуть позже, как будет возможность отправлю.

0

8

Скромный пример. Ещё жутко лажает. Но, уже можно на кнопочки по нажимать. Это просто визуальный пример организации структуры. Ещё не полноценный поиск.

Код:
EnableExplicit

;- Structure
Structure CharDB
  PrevChar$
  ThisIsCharEndWord.a
  Map Child.CharDB()
EndStructure

;- Variable
Global NewMap CharDB.CharDB()

Global CharDB.CharDB

#Win = 0
Enumeration Gadgets
  #String
  #Editor
  #Canvas
EndEnumeration

Procedure _Draw_Child( SearchText$, *Parent.CharDB = 0, cx = 20, cy = 20, NextChar = 0, PrevChar$ = "" )
  ForEach *Parent\Child()
    Protected Char$ = MapKey( *Parent\Child() )
    Protected Color = $C0C0C0
    If Char$ = Mid(SearchText$,NextChar,1) 
      _Draw_Child( SearchText$, *Parent\Child(), cx, cy + 30, NextChar + 1, Char$ )
      Color = $7FFF00
    EndIf
    RoundBox( cx-(TextWidth(Char$)/2)-2, cy-1, 20, 20, 20, 20, Color )
    DrawText( cx, cy, Char$, 0 )
    cx + 30
  Next
EndProcedure

Procedure _Draw_Demo( SearchText$ )
  StartDrawing(CanvasOutput(#Canvas))
  Box( 0, 0, OutputWidth(), OutputHeight(), $FFFFFF )
  Protected cx = 20, cy = 20
  Protected ch = TextWidth("W")
  DrawingMode(#PB_2DDrawing_Transparent)
  Protected Size = MapSize( CharDB() )
  Protected Char$ = ""
  Protected *Next.CharDB = 0
  ForEach CharDB()
    Char$ = MapKey( CharDB() )
    Protected Color = 0
    If Char$ = Left(SearchText$,1)
      *Next = CharDB()
      Color = $7FFF00
    Else
      Color = $C0C0C0
    EndIf
    RoundBox( cx-(TextWidth(Char$)/2)-2, cy-1, 20, 20, 20, 20, Color )
    DrawText( cx, cy, Char$, 0 )
    cx + 30
  Next
  cy + 30
  If *Next
    _Draw_Child( SearchText$, *Next, 20, 70, 2, *Next\PrevChar$ )
  EndIf
  StopDrawing()
EndProcedure

; если есть другой монитор, то окно появится на следующем от основного
Procedure _OpenWindow( Window, X, Y, Width, Height, Title$, Flags = #PB_Window_SystemMenu, ParentWindowID = 0, NumDesktop = 0 )
  If NumDesktop >= 0 And NumDesktop < ExamineDesktops()
    X + DesktopX(NumDesktop) : Y + DesktopY(NumDesktop)
    If Flags & #PB_Window_ScreenCentered
      X + ( DesktopWidth(NumDesktop)  / 2 ) - ( Width  / 2 )
      Y + ( DesktopHeight(NumDesktop) / 2 ) - ( Height / 2 )
      Flags ! #PB_Window_ScreenCentered
    EndIf
  EndIf
  ProcedureReturn OpenWindow( Window, X, Y, Width, Height, Title$, Flags, ParentWindowID )
EndProcedure
Macro OpenWindow : _OpenWindow : EndMacro


; Добавление слова в базу
Procedure Add( Word$ )
  Protected i, *Parent.CharDB = 0, PrevChar$ = ""
  For i = 1 To Len( Word$ )
    Protected Char$ = LCase( Mid( Word$, i, 1 ) )
    If Not *Parent
      If FindMapElement( CharDB(), Char$ )
        *Parent = CharDB()
      Else
        *Parent = AddMapElement( CharDB(), Char$ )
      EndIf
    Else
      If FindMapElement( *Parent\Child(), Char$ )
        *Parent = *Parent\Child()
      Else
        AddMapElement( *Parent\Child(), Char$ )
        *Parent\Child()\PrevChar$ = PrevChar$ ; текущая буква, будет знать о предыдущей
        If i >= Len(Word$)
          *Parent\Child()\ThisIsCharEndWord = #True ; указываем, что это конечный символ в слове
        EndIf
        *Parent = *Parent\Child()
      EndIf
    EndIf
    PrevChar$ = Char$
  Next
EndProcedure  

Procedure _Event_String_Edit()
  _Draw_Demo( GetGadgetText(#String) )
EndProcedure

Procedure _Event_Resize_Wondow()
  Protected w = WindowWidth(#Win), h = WindowHeight(#Win)
  ResizeGadget( #String, #PB_Ignore, #PB_Ignore, w-20, #PB_Ignore )
  ResizeGadget( #Editor, w-210, #PB_Ignore, #PB_Ignore, h-50 )
  ResizeGadget( #Canvas, #PB_Ignore, #PB_Ignore, w-230, h-50 )
EndProcedure

Global NewList Find$()

;- OpenWindow
If OpenWindow( #Win, 0, 0, 920, 620, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget, 0, 1 )
  StringGadget( #String, 10, 10, 900, 20, "" )
  EditorGadget( #Editor, 710, 40, 200, 570, #PB_Editor_ReadOnly )
  
  CanvasGadget( #Canvas, 10, 40, 690, 570 )
  
;   - добавление
  Add("Лазер")
  Add("Лазера")
  Add("Лазерам")
  Add("Лазерами")
  Add("Лазерах")
  Add("Лазере")
  Add("Лазеров")
  Add("Лазером")
  Add("Лазеру")
  Add("Лазеры")
  
  BindEvent( #PB_Event_SizeWindow, @_Event_Resize_Wondow(),#Win )
  BindGadgetEvent( #String, @_Event_String_Edit(), #PB_EventType_Change ) 
  
  _Draw_Demo("")
  SetActiveGadget(#String)
  
  Repeat
    Define Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
  
EndIf

Отредактировано Webarion (09.02.2024 03:19:09)

0

9

Webarion
Появится RadixTree(), он уже есть в бета.

0

10

AZJIO написал(а):

Webarion
Появится RadixTree(), он уже есть в бета.

А я и не знал. Ну хорошо, посмотрим, потестируем. Если будет хорошо работать то и здорово.

Отредактировано Webarion (09.02.2024 04:42:22)

0

11

AZJIO написал(а):

Появится RadixTree(), он уже есть в бета.

Строка 1: RadixTree() не является функцией, массивом, макросом или связным списком.

Это в IDE появился.

0

12

Пётр написал(а):

Это в IDE появился.

В разрабатываемом IDE это есть:
RadixTree.pb
И видно, что есть подключение.
Можно потестировать так:

Код:
; указать своё расположение файла
XIncludeFile "purebasic-devel\PureBasicIDE\RadixTree.pb"

Define Tree.RadixTree

RadixInsert( @Tree, "Текст",    111 ) ; текст и его данные
RadixInsert( @Tree, "Текстов",  222 )
RadixInsert( @Tree, "Текстами", 333 )

; просмотр
Define NewList Test()
RadixEnumerateAll( Tree, Test() )

ForEach Test()
  Debug Test()
Next

Возможно, это просто фича для самой IDE, и никогда в компилятор добавлена не будет ))

Отредактировано Webarion (09.02.2024 15:19:11)

0

13

Жаль, я уже готовился использовать предполагая, что есть функция, которая превратит список в дерево.
Как то я высказался в теме про алгоритм поиска файлов, что проверка расширения по строке работает медленно и быстрее если расширения превратить в список, мне ответил, что он уже сделал что-то в виде RadixTree и это ещё быстрее моего предложения. И при разговоре о RadixTree я подумал, что будет теперь некая карта/список, в которую можно добавить элемент типа AddElementRadixTree() и элемент будет разложен на дерево. Вообще хотелось бы, чтобы был такой инструмент и он был простым и имел простые функции - создать, добавить элемент, проверить наличие элемента. Некоторые словари думаю быстрее бы работали имея такое дерево, так как не пришлось бы проверять весь список, а совершались бы прыжки по указателям. При длине слова 6 символов мы прыгаем максимум через 6 указателей и пробегаемся в среднем по 26/2=13 символов, то есть 6*13=78 шагов, не важно сколько база содержит слов, а если бы пришлось пробежаться по 100 тыс слов, очевидно что это будет работать медленно 100000*6/2 = 300000 шагов.
Ещё бы сохранять/открывать это дерево в бинарном виде, так как создание такого дерева методом вставки слов будет не быстрым.

Отредактировано AZJIO (09.02.2024 19:09:33)

0

14

Можно взять файл RadixTree.pb и использовать в своих проектах.

0

15

Пётр написал(а):

Можно взять файл RadixTree.pb и использовать в своих проектах.

Безусловно!

В этом файле, две процедуры помещены в CompilerIf #False ... CompilerEndIf
Это только вручную переключается, установкой CompilerIf #True.
Интересно, что тот способ, который я предложил, похож на RadixTree, хотя, раньше я даже не знал об этом алгоритме. Видимо, просто не было интереса. Придумал структуру, а по сути оказалось, что это тот же самый RadixTree, только на хэш-карте ) Кажется очевидным, раскладывать слова по буквам.

Попробую своё решение сделать на указателях, думаю, что это будет то же самое, или похоже, потому-что у меня есть дополнительные идеи.

Отредактировано Webarion (12.02.2024 02:09:28)

0

16

Пётр написал(а):

Можно взять файл RadixTree.pb и использовать в своих проектах.

Я толком не разобрался в лицензии, насколько я понял лицензия подразумевает использование функционала если вы распространяете свою программу с исходниками, то есть также свободную и с лицензий что люди должны быть осведомлены, что программа свободна и имеет исходники. Даже если программа продаётся, модифицированные исходники прилагаются, только не понятно на весь ли продукт или только в плане функционала.

0

17

AZJIO написал(а):

Я толком не разобрался в лицензии, насколько я понял лицензия подразумевает использование функционала если вы распространяете свою программу с исходниками, то есть также свободную и с лицензий что люди должны быть осведомлены, что программа свободна и имеет исходники. Даже если программа продаётся, модифицированные исходники прилагаются, только не понятно на весь ли продукт или только в плане функционала.

Просто указывай, что используется RadixTree, под лицензиями GPLv3 и Fantaisie Software License
А твой проект может быть под любой лицензией.
GPL вообще без проблем, а вот с Fantaisie нужно разобраться. Явно написано: "Внося изменения или дополнения в этот проект..."
Думаю, что это касается только PureBasic IDE, как цельного проекта. Используя же одну независимую от проекта библиотеку, ты не вносишь изменений в сам проект, особенно, если не правишь этот файл, так как твой проект, вообще не тот, о котором говорит лицензия.
Вот если было бы сказано "Используя любую часть кода этого проекта, в любом своём проекте, мы имеем право на ваш проект...". Тогда уже нельзя было бы. Хотя в будущем Fantaisie может и изменить лицензию на такую )
Лучше всего конечно написать свой RadixTree, и забыть об этих лицензиях навсегда.

Отредактировано Webarion (13.02.2024 18:18:42)

0

18

Webarion написал(а):

Лучше всего конечно написать свой RadixTree, и забыть об этих лицензиях навсегда.

Если бы этот модуль был встроен в PureBasic, то всё было бы ясно, он бы имел ту же лицензию как и любой код созданный PureBasic`ом.

Webarion написал(а):

а вот с Fantaisie нужно разобраться.

вот как раз тут всё просто, если ты принял участие в разработке IDE, то это уже принадлежит Fantaisie, если не согласен то не пытайся помогать им. Они для того и сделали код открытым чтобы в его разработке участвовали знающие люди. Для себя и для общего блага. А лицензия защита от того что ты потом не подашь в суд, после предоставления какого-то кода для IDE, который был туда встроен.

Другое дело использование кода за пределами проекта, ведь могут найтись люди, который тупо скопируют все целиком и скажут что это их продукт, вот от этого и защита, что предоставленный код не окажется в чьих то руках как собственность. Ну и хотелось бы явно увидеть возможность использования частей кода в коммерческих проектах. Если это не описано понятным языком, то надо принимать это как недоступное. То есть я могу использовать при условии распространения своего кода открытым и ещё уведомить об этом пользователя или продать но обязательно уведомить что код открытый и то можешь взять его и скомпилировать. Также я могу предположить что если бы было разрешение брать код для коммерческой выгоды, то также любой может взять весь IDE и выдать за свой коммерческий проект, наверняка авторы этого бы не хотели. То есть адекватно разрешение такого не должно быть, можно только надеяться на чудо, которого я пока явно не нашёл в лицензии. Но не исключаю, что можно спросить авторов об этом и они всё скажут включая индивидуальное разрешение на какую либо часть с иными условиями. У меня прям нет сейчас в этом необходимости, но если вдруг возникнет желание продать прогу, а в ней окажется такой код, будет проблема, поэтому и хочется чтобы всё было изначально разрешено. Долго вкладывать огромные усилия, а потом понять что имеешь ограничение это неприятный риск. Когда-то я в винду так вкладывался и ещё окажешься виноватым, но теперь ушёл от этого.

Отредактировано AZJIO (13.02.2024 19:52:23)

0

19

Сделал Стеминг для AkelPad, теперь надо переписать его на SpiderBasic.

0


Вы здесь » PureBasic - форум » PureBasic для Windows » как автоматически удалить окончание в словах