Я как всегда об APL, а точнее о старой версии языка K, которая содержала в себе GUI, с весьма необычным подходом к нему.
К сожалению новые версии языка K решили сосредоточиться на обработке данных и исключили GUI, так что данный подход остался в истории, однако может быть кто-то подскажет аналогичные современные фреймворки — было бы очень интересно посмотреть.
Начнём. Краткое описание API, которое нам доступно:
`show$`v show variable v
`hide$`v hide variable v
Это всё, больше нет ничего. Т.е. основная особенность — что GUI в K это прямое отображение данных в памяти. А теперь как с этим можно удобно работать.
Для начала попробуем самое простое:
C:>k
K 3.1 2004-01-28 Copyright (C) 1993-2004 Kx Systems
WIN32 2CPU 4030MB ws-1341.x.com 0 EVAL
a:10 _draw 100 / list
a
20 51 12 34 31 51 29 35 17 89
`show$`a
На экране появляется таблица со списком
Любое значение редактируемое, меняем 35 на 135 и это изменение сразу меняет значение в списке:
a
20 51 12 34 31 51 29 135 17 89
Если меняем значение в списке, то оно тутже обновляется в интерфейсе.
Вывести небольшой список не проблема, а что если данных будет много? пусть будет очень много:
a:(10 10000000) _draw 100 / 10 списков по 10 миллионов каждый
`show$`a
Никаких проблем — всё быстро отображается, прокручивается и редактируется.
Именуем колонки: логично что имя колонки — это ключ, а список — значение из hashtable:
t:.((`a;10 _draw 100;);(`b;10 _draw 100))
`show$`t
Но это всё очень просто, смотрим что есть ещё. аттрибуты и триггеры — по сути это просто ключ-значение в hashtable, привязанном к переменной, в зависимости от того есть такое значение или нет — происходят разные действия:
Самый простой пример: добавление label.
val:10
val..l:"Input field"
`show$`val
Следующие аттрибуты влияют на отображение в gui:
Display attributes (for variables that have class).
x width integer(KFONT width)
y height integer(KFONT height)
a arrangement nest of symbols(class `form)
o options list of symbols(class `radio)
l label string
kl click label string (also klr)
Data-display attributes (for variables that have class `data).
functions (monadic, constant or array) default
e editable 0 or 1 1
f format string from data 11$(11.2$)
g getdata data from string 0$ etc.
u update update[old;new] :
fg foreground integer(rrggbb) 0
bg background integer(rrggbb) -1(808080)
expressions/events (strings)
ins, del, f1 ... f12, ctrl_a ... ctrl_z
k, kr, kk click, click right, double click(precludes e)
c class(display) symbol
`data(default) atom, list, dict, list of lists, dict of lists
`chart as above where atom is list of y values
`plot as above where atom is matrix of (x;y) values
`check 0 or 1
`radio symbol (one of ..o; see below)
`button expression or dictionary of expressions
`form dictionary of entries of any class(incl. `form)
Не буду описывать детальные особенности каждого из аттрибутов, так как большинство из них очевидны из описания и делать подобие мануала не хочется.
Для простоты создания словаря — язык позволяет работать в контексте внутри словаря — т.е. аналогия с папками и модулями, которые просто хэш-таблицы:
d form
val:100
incr:"val+:1"
incr..l:"Increment"
decr:"val-:1"
decr..l:"Decrement"
incr..c: decr..c: `button
d ^
form / хэш ключей-значений и хэшей аттрибутов.
.((`val;100;)
(`incr
"val+:1"
.((`l;"Increment";)
(`c;`button;)))
(`decr
"val-:1"
.((`l;"Decrement";)
(`c;`button;))))
Аттрибутом ..a установим порядок отображения.
form..a:`incr`val`decr
form.val..e:0 / отключаем редактирование значения.
..a может быть любой формы. Например добавим пару кнопок.
form.incr10:"val+:10"
form.decr10:"val+:10"
form.incr10..c : form.decr10..c: `button
form..a:(,`incr;`decr10`val`incr10;,`decr)
form..a
(,`incr
`decr10 `val `incr10
,`decr)
Т.е. смысл в том, что всё GUI описывается примитивными структурами языка и является их же прямым отображением. Можно включать hash в hash, т.е. включать форму в форму компонуя элементы и тд.
Ну а теперь самое интересное, а именно несколько примеров:
Калькулятор:
calc..a:(`exp
`va`vb`vc`vd
`n0`n1`n2`n3
`n4`n5`n6`n7
`n8`n9`lp`rp
`fa`fs`fm`fd`fe
`eval`clear) / порядок элементов на форме
calc:@[_n;1_-1_ calc..a;:[;"exp,:(~_v)`l"]] / expressions
calc[.;`l]:"abcd0123456789()+-*%:" / labels
calc.eval:"exp:5:. exp"
calc.clear:"exp:"""
calc[.;`c]:`button
calc.exp:calc.exp..l:""
`show$`calc
Можно рисовать графики:
p..c:`chart
p:(5 5) _draw 100
`show$`p
Или высокохудожественная мазмя:
`show$.,((`p;({[x] (2 30)_draw 30}'!10);.,(`c;`plot;)))
А теперь удивительная вещь, которая когда-то так удивила своей краткостью и понятностью (при минимальном знании словаря конечно), что я решил начать изучать K глубже:
Broadcast сервер:
d:10 _draw 10
w:!0 / empty client list
.m.g:{w,:_w;d} / return data
.m.c:"w@:&~w=_w" / retain clients
.m.s:{. x;w 3::x;} / (log `l 5:,x) apply and broadcast
m i 1 listen on port 1
Клиент:
h:3:(`;1) / connect to server
d:h 4:_n / get database
d..t:"if[0=_w;h 3:(_v;_i;:;_v ._i)]" / send my updates
`show$`d
Вот эти несколько строчек создают сервер с простым списком в качестве данных. А сколько угодно клиентов присоединяются к нему и имеют совместно редактируемый список с обновление в реальном времени, всего 9 строчек. Подобный код (без GUI конечно) в настоящее время используется на многих крупнейших биржах для транспортных и балансировочных узлов, которые обслуживают инстансы баз данных Q (новая версия K).
Картинка конечно не передаёт того как это работает в динамике.
Автор: inv2004