· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.HEAP —› Горячие клавиши

Посл.отвђт Сообщенiе


Дата: Сен 15, 2004 03:07:41 · Поправил: PavPS

Доброе всем времечко!

Речь идет об алгоритме обнаружения(обработки, точнее) горячих клавиш. А ещё точнее - их последовательности. Вот, например есть процедура, которой передаётся 1 параметр-ASCII код. ну и при достижении определенных ключевых клавосочетаний чё-то выполняет.
Так вот, хотелось бы узнать о различных реализациях, мотодах для решения этой задачи. Может ссылки, может ищо чё. Естественно упор делается на большую скорость, малый объем памяти и бесконечное(ну большое) ветвление.

Заранее благодарен. --==PPS==--

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


Дата: Сен 15, 2004 04:16:17 · Поправил: S_T_A_S_

Про различные методики я мало что знаю, но делал примерно так:

- ASCII код помещаем в FIFO буфер (размер - максимально возможная последовательность);

- содержимое буфера проверяем на предмет совпадения с данными в таком примерно массивчике:

db 'a','b','c','d','f','g',0,0 ; последовательность клавиш
dd abcdfg_handler

db 'a','c','d','b',0,0,0,0
dd acdb_handler

dd 0 ; данных больше нет
........

- если есть совпадение, то вызываем соответствующий обработчик.


Поиск выполнять можно быстро, если сравнивать не по-символьно, а сразу по 4 (обычный регистр) или по 8 (mmx).
Размер регистра выбирается в зависимости от максимальной длины ключевой последовательности.

Если ASCII последовательности имеют различную длину (а это скорее всего так и есть), необходимо будет перед сравнением накладывать маску на операнды, что бы сократить количество сравниваетмых символов до необходимого.

Вот примерный код поиска (правда, требует P III, использую в другом месте, но вроде бы и сюда подходит :).
; EAX = длина искомой последовательности.
; ESI - указатель на искомую строку
    neg     al
    add     al,8
    shl     eax,3
    movd    mm0,eax
    pcmpeqd mm1,mm1
    psrlq   mm1,mm0
    movq    mm0,mm1
    pand    mm1,QWORD[ESI]

    mov     ecx, msg_handlers_array ; см. выше
search:
    cmp     DWORD[ECX],0
    jz not_found
    movq    mm2,QWORD[ECX]
    add     ecx,12
    pcmpeqd mm2,mm1
    packssdw mm2,mm2
    movd    eax,mm2
    inc     eax
    jnz     search;
    jmp     DWORD[ecx-4] 



PS
Зачем тему в HEAP поместил.


Дата: Сен 15, 2004 16:03:22

Спасибо!
Теперь что у меня:

1)
есть связанный список с дочерними элементами (черное дерево, если не ошибаюсь):
2-х сторонняя связь и отдельное поле для дочернего дерева+8 байтное поле для ASCII кода и арес ф-ии, который надо выполнить, если на этом этапе ASCII совпал. Дочерний элемент указывает на новую часть (ступень N-мерного дерева) с новыми клавосочетаниями.

2)
Затем существует двухсторонний связанный список (нет дочерних элементов),каждый элемент которого указывает на текущую позицию в дереве с клавосочетаниями.

Этот способ учитывает вроде бы все требования, но тормоз:
Приходится смотреть все элементы 2-го дерева+для каждого элемента 2-го дерева попасть на ветвь N-мерного дерева и там проверить следующую букву. Короче, легче, конечно на картинке объяснять.

Вариант у S_T_A_S_ , не обработает промежуточные команды, да и там придется запоминать прошлую позицию, для того, чтобы считать каждую букву первой в какой-то строке.
Так, например
1)123Пуск
2)1123Пуск
2-й вариант будет ошибочным, хотя он верный.

При так к примеру клавосочетания типа
BeepOn
BeepOff
BeepModeVolUp
BeepModeVolDown
BeepModeVolOff
BeepMode1
BeepMode2

Вариант простого сравнения строк будет не очень, т.к. надо иметь в памяти посл-ть из последних нажатых символов длиной в длину максибально длинного ключевого слова «BeepModeVolDown», а при использовании деревьев быдет основание «Beep» , дочерний элемент последней буквы будет дерево №1 «On» №2 «Mode» у него на конце – ответвление «VOl». И т.д.

По моему мнению, если усовершенствовать метод простого сравннеия можно придти к этому, и к какому-нить другому.

Так вот и хочется узнать какой-нить по короче, но без недостатков(или с перечисленными приемуществами), и быстрый, и шоб памяти поменьше. Может те же хэш-или ещё какие таблицы… Может код у ког какой есть.

З.Ы. S_T_A_S_, всё выше перечисленной не в укор, а просто очень хочется найти ответ на этот вопрос, и я просто сравнивал. Этот метод, конечно, ввиду использования места в памяти для структур деревьев памяти жрет тоже нефигово. А в Heap это я по ошибке создал. Если «Даос»-у будет угодно, то перенесёт и в другой раздел.


Дата: Сен 16, 2004 05:18:14

Какие проблемы использовать буфер с длиной равной длине максимально длинного ключевого слова?
Это IMHO упрощает программу - нет необходимости в сложных структурах (сохраним деревья для детей ;), запоминании предыдущих состяний (они уже хранятся в буфере)


Вот вариант №2 (усовершенствовенный)

- клавосочетания делим на группы по количеству символов;

- адреса групп храним в массиве:
........
dd klavasochetanie6
dd klavasochetanie7
dd 0; нет клавосочетаний такой длины

- берём последние N символов из буфера;

- ищем в соответствующей группе, если группа существует;

- 2 предыдущих пункта повторяем для N+1;


На первый взгляд слишком много поисков придётся делать.. вроде бы плохо..
Но макс. длина последовательности - 15 байт.
Это максимум 4 сравнения обычными регистрами, циклы никакие не нужны.
Такое сравнение (в самом худьшем случае) будет быстрее, чем вычисление хэша от строки! (причём в случае с хэшами бинарного поиска всё равно не избежать).
Ещё бы хорошо отсортировать данные в группах по алфавиту -
так можно избежать проверок лишних элементов.
Или сравнивать строки начиная на сначала, а с конца.

Реализация этого на асме довольно проста будет.
Если делать на "Си в стиле Си", то может очень сильно усложниться :(

По поводу размера самих данных. Деревья хранят указатели - а один указатель - это 4 символа.
Тут экономия IMHO обманчива.
Если же размер критичен (последовательностей слишком много), можно применить примитивное сжатие строк - выделить несколько токенов (например "Beep", "Mode") заменить их на 01, 02,... во время инициализации проги "распаковать" данные.


> „Так, например
1)123Пуск
2)1123Пуск
2-й вариант будет ошибочным, хотя он верный. “


Буфер Firs In - First Out. Данные дописываем в конец, при этом смещаем содержимое "влево".
Подстроки нужно брать не от начала буфера, а от (конец_буфера - длина_подстроки).
Тогда проблем никаких нет.


ЗЫ
Если нужно, накидаю примерный код, только напиши на чём лучше.. Я щас подобным занимаюсь, но поиск у меня в текстовых файлах, которые я терпеть не могу =)


ЗЫЫ
В HEAP некоторые не заглядывают, так что возможно поэтому тут нет кучи ссылок на методы реализации :).


Дата: Сен 16, 2004 14:38:52

Благодарю!

По поводу обманчивости в объеме, то да, это действительно так. И знаешь, действительно, надо бы попробывать написать таким способом. Только вот, на мой взгляд, надо бы чуть усовершенствовать:

;*************************************CODE**************************** ***
KeystrokesArray:
SetBootWW :
db ‘S’ dd 0
db ‘e’ dd 0
db ‘t’ dd 0
db ‘B’ dd 0
db ‘o’ dd 0
db ‘o’ dd 0
db ‘t’ dd Offset WriteBoottVariants
db ‘A’ dd 0
db ‘W’ dd Offset SetBootAfterWindows
SetBootM:
db ‘S’ dd 0
db ‘e’ dd 0
db ‘t’ dd 0
db ‘B’ dd 0
db ‘o’ dd 0
db ‘o’ dd 0
db ‘t’ dd Offset WriteBoottVariants
db ‘M’ dd Offset SetBootManual
SetBootBW:
db ‘S’ dd 0
db ‘e’ dd 0
db ‘t’ dd 0
db ‘B’ dd 0
db ‘o’ dd 0
db ‘o’ dd 0
db ‘t’ dd Offset WriteBoottVariants
db ‘B’ dd 0
db ‘W’ dd Offset SetBootBeforeWindows

WriteBoottVariants Proc
Write (‘
Boot with windows – WW,
Boot before windows – BW,
Boot manual – M,
’);
Ret
WriteBoottVariants EndP
;********************************************************************* ***

Пояснение
Т.е при совпадении уже набранного буфера до «SetBoot», запускается процедура вывода на экран подсказки о дальнейших вариантах. После чего пользователь набирает нужное ему окончание. По достижении этого окончания вызавается соответ-ая ф-я.
Есть тута один недостаток: скока раз повторилось «BootSet»??! Хотя, да, упаковать примитивно действительно – идея. В программе у меня очень много команд именно с таким интерфейсом. И эти повторения я избежал, создав деревья.

Чё та мне стало интересно по поводу объёмов данных.
Давай подсчитаем:

===
_статических_
===

В приведенном выше примере размер структуры: 26*5=130 байт.
При использовании деревьев – 12*5=60 байт.

===
_динамических_
===

В приведенном выше примере размер: так же 130 байт + размер рабочего буфера =9+1.
Итого: 140 байт

Дерево клавиш: (1+4+4+4+(01+04))*12
Дерево текущих направлений: (1+4+4+4)*(до 12, но всреднем - 3) При безошибочном наборе ==2

Суммируем:255.

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

;**********************************1-типная*************************** ******
Рассмотрим однотипную (только про один вид команды, например про ту же загрузку) и 2-х уровневую последовательность команд.

(2-х уровневая означает, что не так: BootGet1,BootGet2,BootGetDate1,BootGetDate2 – это 3-х уровневая., а если добавить BootGetDateWhenSet1, BootGetDateWhenSet2 – это 4-х уровневая)

1)Пусть первый уровень содержит X букв (по 4(для адр ф-й) + «А»(для элемента сравнения, т.к. ASCII – это 1 байт, а код от клавиатуры=SCANCODE+FLAGS=4 байта) байт в каждом)
2)Пусть второй уровень содержит Y букв (4+A).
3)Пусть команд будет С штук.

V – объем в байтах.

Для буферного метода: V(X)=С*[X + Y]*(A+04) + [X+Y+1] (это буффер)

Для метода c деревьями: V(X)= (1+4+4+4+(04+A))*[X + С*Y] + (X+ С*Y)*(3 - всреднем)

;***********************************2-х типная*******************************

Для буферного метода: V(X)=
С1*[X1 + Y1]*(A+04) + С2*[X2 + Y2]*(A+04) + [X+Y+1] (это буффер)


Для метода c деревьями: V(X)=
(1+4+4+4+(04+A))*[X1 + С1*Y1] + (X1+ С1*Y1)*(3 - всреднем)
+
(1+4+4+4+(04+A))*[X2 + С2*Y2] + (X2+ С2*Y2)*(3 - всреднем)

;**************************************3-х уровневая**************************
;**********************************1-типная*************************** ******

4)N - Кол-во команд, ушедших до 3-го уровня. Т.е на 2-м уровне осталось С-N команд.
5) Пусть Z – кол-во символов на 3-м уровне.

Для буферного метода: V(X)=
(С- N)*[X + Y]*(A+04) + N*[Z + X + Y]*(A+04) + [Z+X+Y+1] (это буффер)
Для метода c деревьями: V(X)=
(1+4+4+4+(04+A))*[X +(С-N)*Y + N*Z] + (X+ (С-N)*Y + N*Z)*(3 - всреднем)


В аттаче таблица с данными.

З.Ы.:
Самому было интересно, вот и проверил. Вроде не ошибся. Т.О. при >2 – х уровневых командах, уже встречаются приравнивание размеров динамических структур. Статические – конечно же меньше у метода с деревьями. Но вот, всё в чём проблема – так это очень маленькая скорость метода с деревьями. Решил, когда допишу до конца метод с деревьями, заняться буферным.

Надеюсь на комментарии. --==PPS==--


1801042346__KeyStrokes.xls


Дата: Сен 17, 2004 11:49:01

Хм.. ну уже математика пощла :)
Я вот накидал рабочий примерчик - у меня данные немного по-другому хранятся, соответственно весят немного меньше (учитывая возросшее кол-во полезных данных).
+ Код 106 байт. (осталось только добавить заполнение буфера).
Работает довольно быстро - за работой наблюдать в отладчике.

        ; fasm
        format pe
        entry  check

                ;  '0123456789abcdef'
input_buffer    db 'xxxxxxxBeepMode2'

        align   4

size__6:        ;89abcdef
        dd      handle_BeepOn
        db      '..BeepOn'
        dd      0

size__7:        ;89abcdef
        dd      handle_BeepOff
        db      '.BeepOff'
        dd      0

size__8:        ;89abcdef
        dd      show_tip_BeepMode
        db      'BeepMode'
        dd      0

size__9:        ;456789abcdef
        dd      handle_BeepMode1
        db      '...BeepMode1'
        dd      handle_BeepMode2
        db      '...BeepMode2'
        dd      0

size_10:
size_12:
        dd      0

size_11:        ;456789abcdef
        dd      show_tip_BeepModeVol
        db      '.BeepModeVol'
        dd      0

size_13:        ;0123456789abcdef
        dd      handle_BeepModeVolUp
        db      '...BeepModeVolUp'
        dd      0

size_14:        ;0123456789abcdef
        dd      handle_BeepModeVolOff
        db      '..BeepModeVolOff'
        dd      0

size_15:
        dd      handle_BeepModeVolDown
        db      '.BeepModeVolDown'
        dd      0

map     = $ - 4*6
        dd      size__6, size__7
        dd      size__8, size__9
        dd      size_10, size_11
        dd      size_12, size_13
        dd      size_14, size_15


check:
        push    15
        pop     ebx

check_ebx_keys:
        mov     esi, [map+ebx*4]
        mov     ecx, ebx
        ; mask
        shl     ecx, 3
        xor     edx, edx
        dec     edx
        shr     edx, cl
        not     edx
        ; input buffer address
        mov     edi, 16+input_buffer
        sub     edi, ebx
        and     edi, -4
        ; switch
        mov     ecx, ebx
        neg     ecx
        and     ecx, -4
        lea     ecx, [ecx*2+8*4+@f]

check_sequence:
        cmp     dword[esi],0
        jz      check_ebx_next
        jmp     ecx

check_next_sequence:
        lea     esi, [esi+ebx+3+4]
        and     esi, -4
        jmp     check_sequence

check_ebx_next:
        dec     ebx
        cmp     ebx, 6
        jnc     check_ebx_keys
        ret

@@:     mov     eax, [edi+0xC]
        cmp     [esi+0xC+4], eax
        jnz     check_next_sequence

        mov     eax, [edi+0x8]
        cmp     [esi+0x8+4], eax
        jnz     check_next_sequence

        mov     eax, [edi+0x4]
        cmp     [esi+0x4+4], eax
        jnz     check_next_sequence

        mov     eax, [edi]
        and     eax, edx
        and     edx, [esi+4]
        cmp     edx, eax
        jnz     check_next_sequence

        jmp     dword[esi]


handle_BeepOn:
handle_BeepOff:
show_tip_BeepMode:
handle_BeepMode1:
handle_BeepMode2:
show_tip_BeepModeVol:
handle_BeepModeVolUp:
handle_BeepModeVolOff:
handle_BeepModeVolDown:                     


1342120725__0.EXE


Дата: Сен 19, 2004 19:33:14

Благодарю!!! Учту такой вариант реализации этого алгоритма. По размерам и скорости... мда.. - базара нэма.


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.087