Войти
 
 
   
 
  
Новости Notes.ру Библиотека Биржа труда Вопрос - ответ Форум Регистрация Поиск О проекте
Разделы
О Notes
Советы
Шаблоны и примеры
Литература
Презентации
 
Всё о задаче AdminP. Часть вторая   Во второй части мы завершаем рассмотрение AdminP. В ней рассмотрены запросы междоменного администрирования и способы управления функциями AdminP с помощью настроек документа сервера, команд консоли сервера, файла Notes.ini и интервалов очистки базы данных. В этой статье предполагается, что вы опытный администратор Domino и прочитали первую часть
О Notes Читать статью
 
Всё о задаче AdminP. Proxy-действия в R5 и Domino 6   Приложение к статье об административном процессе
О Notes Читать статью
 
Всё о задаче AdminP. Часть первая   Перевод классической статьи 2003-его года о задаче административного процесса (AdminP). Очень полезна для понимания работы механизма этой задачи. В первой части статьи описаны компоненты задачи AdminP, как они работают, и как их использование помогает сделать работу администратора Domino проще. Задача AdminP (сакращённо от Administration Process, Административный процесс) работает с базой административных запросов (Administration Requests, admin4.nsf)
О Notes Читать статью
 


Советы

Главная   Библиотека   Советы

Язык @-формул: Per aspera ad astra. Часть первая

Николай Норкин,
Вятские Информационные Технологии, Киров
При всей мощи языков LotusScript и Java решения на основе языка @-формул зачастую незаменимы, сильны и элегантны. Однако перед тем, как "дотянуться до звезды", приходится буквально пробираться через земные преграды. Впрочем, тропка здесь уже протоптана

Логические операции над списками. Довольно часто при написании кода используются операции сравнения. Ничего из себя страшного не представляющий, к примеру, код, используемый в формуле скрытия абзаца Hide when: <значение_поля> = <переменная> имеет, казалось бы, довольно прозрачную альтернативу - <значение_поля> != <переменная>

Проблемы начинаются, когда хотя бы один из операндов имеет списковое значение. Сперва стоит разобраться с логикой операции сравнения для случая, когда несписковый операнд сравнивается со списочным, например, часто используемая на практике формула скрытия кнопки панели действий (или параграфа) в зависимости от роли: @UserRoles = "[Supervisor]"

При таком использовании оператора сравнения значение неспискового операнда сравнивается с каждым значением списка, причем для общего положительного результата достаточно положительного результата в одной паре сравнения. Можно сказать, что над результатами сравнения каждой пары значений выполняется операция дизъюнкции (логического ИЛИ):

(<значение> = <первое значение списка>) | (<значение> = <второе значение списка>) | .... | (<значение> = <последнее значение списка>)

Как видно, если одно из значений списка равно значению одиночного операнда, операция возращает положительный результат (@True), что вполне удовлетворительно для условий скрытия. Иначе ведет себя операция "неравенства", скажем для случая, когда необходимо скрыть кнопку действия от всех пользователей кроме обладающих ролью [Supervisor]: @UserRoles != "[Supervisor]"

(<значение> != <первое значение списка>) | (<значение> != <второе значение списка>) | .... | (<значение> != <последнее значение списка>)

В этом случае, если функция @UserRoles возвращает более одной роли, результат сравнения в любом случае будет положителен. Действительно, не может же одно и то же значение быть равно всем различным значениям списка. Поэтому сравнение хоть одной из пар неравенств даст положительный результат, что приведет к "всегдашнему" возвращению положительного результата (@True) в результате операции сравнения. В выбранном примере единственный раз, когда операция сработает "правильно", это если пользователю предоставлена только роль [Supervisor] (ситуация вырождается в рассмотренный ранее случай - оба операнда несписковые), при усложнении таблицы доступа к базе данных операция сравнения перестает работать в согласии с логикой разработчика

В формулу скрытия, гипотетически рассматриваемую в качестве примера, следует внести исправления. На данном этапе возможно решение, связанное с тем, что выражение "равенства", рассмотренное выше, в должной мере удовлетворяло логике скрытия. Следовательно, формулу "неравенства" следует переписать следующим образом: !(@UserRoles = "[Supervisor]"). Тогда при наличии у пользователя роли [Supervisor] выражение сравнения возвратит @True, что в результате инверсии приведет к необходимому результату

Все рассмотренные выше случаи можно считать вырожденными для общей формулы сравнения двух списковых операндов: <первый список> = <второй список>. При попарном сравнении первый элемент первого списка сравнивается с первым элементом второго списка, второй - со вторым и т.д. Если один из списков короче другого, вместо недостающих элементов короткого списка используется его последний элемент (сравни предыдущие случаи, когда первый список состоит из одного элемента - этот элемент сравнивается со всеми "лишними" элементами списка):

(<1-е значение> = <1-е значение>) | (<2-е значение> = <2-е значение>) | .... | (< n-е значение> = <n-е значение>) | (< n-е значение> = <n+1-е значение>) | ...

Вследствие этого, значение имеет также и положение элемента в списке. Так, операция сравнения "1" : "5" = "5" : "1" возвратит @False, в отличие от сравнения списков "1" : "5" = "5" : "1" : "5"

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

  • Операция сравнения списков поэлементно каждый элемент с каждым, знак сравнения предваряется значком звездочки (*=, *>, *<) обеспечит положительный результат (@True), если хотя бы одно из значений первого списка присутствует во втором. Порядок элементов в списке здесь уже не имеет значения
  • Функция @IsMember(<первый список>; <второй список>) возвратит положительный результат, если все значения первого списка присутствуют во втором. На практике можно использовать и функцию @IsNotMember

Резюме. При сравнении скалярного значения со списком значений необходимо применять !(<значение> = <список>) или @IsNotMember(<значение>; <список>) вместо <значение> != <список>

При сравнении двух списков в зависимости от логики выражения возможно использование попарного сравнения (=), сравнения все элементы со всеми (*=) или использования функции @IsMember

Использование функции @Replace для сравнения списков. В ограниченном, но встречающемся, числе случаев, описанные выше функции сравнения хотелось бы расширить. Так, ни одно из вышеприведенных решений сравнения списков не применимо, если положительный результат определяет, скажем, наличие не одного одинакового элемента в списках, а трех. Также, эти решения неприемлемы, если в результате необходимо получить не просто признак наличия или отсутствия совпадений, но и список совпадающих элементов

Для решения этих задач можно использовать функцию @Replace

Замена элементов второго списка, встречающихся в первом списке, на пустой элемент ("") с последующим применением функции @Trim даст список "уникальных" элементов первого списка:

UniqueList := @Trim(@Replace(<первый список>; <второй список>; ""))

Повторное применение функции даст искомый список совпадающих элементов:

Result := @Trim(@Replace(<первый список>; UniqueList; ""))

Применение к результату этого выражения функции @Elements с последующим сравнением с "пороговым значением" позволит решить первую задачу:

@Elements(Result) >= 3
В настоящем материале автор не рассматривает возможности использования появившихся в версии 6 Domino @-формул для организации циклов
Получение списка N элементов от 1 до N. На время оставив в стороне сравнение списков, уделим должное внимание ряду решений, которые очень часто оказываются незаменимыми, но вместе с тем достаточно элегантны при скудости используемых для их решения средств

Для получения списка от 1 до N используется строковое сложение (конкатенация) списков "каждый элемент с каждым" (оператор *+):

List0 := "0" : "1" : "2" : "3" : "4" : "5" : "6" : "7" : "8" : "9";
List1 := List0 *+ List0; REM "Результат - строковый список от 00 до 99. Для получения более длинного списка - List1 := List0 *+ List0 *+ List0;";
List2 := @TextToNumber(@Subset(List1; N)); REM "Результат - числовой список от 0 до N - 1";
List2 + 1; REM "Числовой список от 1 до N"

Один из примеров практического использования этого решения представлен в материале >>>

Расположение элементов списка в обратном порядке. Использование функции @Replace вкупе с решением предыдущего примера дает не менее элегантное решение задачи "инверсии" списка (расположения списка в обратном порядке)

Для этого полученный список List2 проинвертируем путем поэлементного вычитания из N - 1:

List2a := N - 1 - List2; REM "Список от N - 1 до 0";
@Replace(@Text(List2); @Text(List2a); <список>); REM "И последние станут первыми";

Продолжение >>>

От автора. Став классикой, код примеров, использованных в настоящем материале, тиражировался так часто, что найти автора решений непросто. Большинство примеров кода найдены в материалах форума компании Интертраст. И все же не хотелось бы ограничиваться "народным" статусом авторства. Если уважаемые коллеги узнают в примерах собственные творения, просьба сообщить автору материала на mailto:notesnet@notesnet.ru. По этому же адресу можно направлять шедевры использования @-формул для их "обессмерчивания"
 
  Опубликовано — 01/05/2004 |    

Constantin, 06.01.2004:
2Автор: Завсегда пожалуйста Конечно, N ограничено (Денис даже указывал значение: почему-то около 1500 а не 32К/8=4096). А способ генерации последовательности конкатенацией строк мне не нравится по чисто вкусовым соображениям - надо иметь оценку верхнего значения N

Автор, 06.01.2004:
Спасибо, Константин! Замечания к твоим комментариям: По 1-ому решению. @Keywords содержит третий параметр. Если удастся в нем указать _заведомо_ не содержащийся в списках символ - можно решить проблему "многословности" По 2-ому решению. Его я тоже считаю "шедевром"... спасибо тебе и Денису... Я бы также включил его в сокровищницу... Единственное, что хочется заметить при сравнении методов... так это "произвольность N"... Объем списка ограничен (32 или 64K?), значение даты - 8 байт... строка из 4 символов - 4 байта... Или под _произвольностью_ понималось что-то другое? Кстати, а что эффективнее - сложение "каждый с каждым" с последующим преобразованием типа или деление на константу?

Constantin, 06.01.2004:
2. Другой способ получения последовательности 1..N (для произвольного N, в отличие от..) d1:=@Date(@Now); 'Можно бы и @Date([1.1.2000]), но тогда надо помнить разделитель и порядок компонент, а так... d2:=@Adjust(d1; 0;0;N; 0;0;0); 'Теперь самое главное: dd:=@Explode(@TextToTime(@Text(d1)+"-"+@Text(d2))); - список дат заданной длины! (dd-d1)/86400+1 Этот "велосипед" я изобрел сам, но Денис его опубликовал раньше: http://www.intertrust.ru/Site/itforum.nsf/all/3D34B428FAC0CF1BC3256A490031A10A?OpenDocument

Constantin, 06.01.2004:
1. А "список совпадающих элементов" гораздо удобнее получать по @KeyWords а не двукратным @Replace. И только для "многословных" элементов списка действительно необходимы @Replace-ы



Добавить комментарий
Имя * :
e-mail
Комментарий * :
Код подтверждения * :


Мероприятия
Пресс-релизы
Биржа труда
Последнее на форуме
 
А так же:
Как удалить профиль?
16.04.2016 00:08:51
Скопировать в буфер поле документа
24.05.2015 08:55:52
Импорт DXL-описания документов в Lotus Domino. Одноимённые поля
16.04.2015 16:49:58
 
© LOGOSPHERE.RU