Система описания химических формул для WEB.

Макрокоманды

Руководство по использованию макрокоманд easyChem


В данной статье даётся обзор всех возможностей макрокоманд CharChem и приводятся примеры.
Макрокоманды (или макросы) позволяют автоматизировать описание однотипных компонент. Хотя они не вносят никаких специфичных способов описания химических структур, зато позволяют повысить эффективность применения всего инструмента в целом.
Для лучшего понимания предлагаемых примеров желательно заранее ознакомиться с методикой описания структурных формул.

Краткий справочник / Оглавление

Шпаргалка - в сжатом виде основные синтаксические конструкции. Плюс ссылки на более подробное объяснение.
Действие Пример синтаксиса Выход пост-обработки
Простейшее объявление @:MyMacro()...some text...@;
Применение @MyMacro() ...some text...
Одновременно объявление и применение @MyMacro()...some text...@() ...some text...
Использование позиционных параметров @:MyMacro(a,b)...&a...&b...@(First,Second) ...First...Second...
Использование именованных параметров @:MyMacro(a,b)...&a...&b...@(b:Second,a:First) ...First...Second...
Значения по-умолчанию @:MyMacro(a:First,b:Second)...&a...&b...@()
@MyMacro(,Last)
...First...Second...
...First...Last...
Вложенные вызовы @:MyMacro(a,b)[&a...&b]@(@MyMacro(A,B),@MyMacro(C,D)) [[A...B]...[C...D]]

Создание простейшего макроса и его использование

Допустим, нужно описать взаимодействие уксусной кислоты с щёлочью.
$C()H3C-C<//O>\O-H + NaOH -> $C()H3C-C<//O>\O-Na + H2O <=> H3C-C<//O>\O^- + Na^+ + H2O

Несложно заметить, что здесь три раза повторяется изрядный кусок молекулы уксусной кислоты. Давайте вынесем его описание в виде макроса.
Детали описания макрокоманды выделены жирным шрифтом.

@:Ac()$C()H3C-C<//O>\O@;
@Ac()-H + NaOH -> @Ac()-Na + H2O <=> @Ac()^- + Na^+ + H2O

Здесь мы видим, что макрокоманда описывается при помощи конструкции @:Name() . . . @;
А используется при помощи @Name(). В качестве имени может выступать слово, начинающееся с латинской буквы.
При использовании нужно учитывать регистр букв. То есть, ABC и Abc - разные имена.

Объявленный макрос можно использовать далее в тексте. Он является глобальным определением. Например, мы можем использовать его в другой формуле:

@Ac()-CH3

Одновременное объявление и использование макроса

Существует способ одновременно описать и использовать макроопределение. Для этого нужно в конце определения вместо @; использовать @().
Наш предыдущий пример может быть переписан так:
@:Ac()$C()H3C-C<//O>\O@()-H + NaOH -> @Ac()-Na + H2O <=> @Ac()^- + Na^+ + H2O

Использование параметров

Следующим естественным шагом использования макросов является применение параметров. Пример с уксусной кислотой может быть переписан так:
@:Ac(x)$C()H3C-C<//O>\O&x@(-H) + NaOH -> @Ac(-Na) + H2O <=> @Ac(^-) + Na^+ + H2O
Здесь мы видим параметр x. Он объявлен в скобках, а используется с помощью &x. Названием параметра может быть имя, начинающееся с латинской буквы, далее могут следовать другие буквы или цифры.
Впрочем, в этом примере мы мало что выиграли от использования параметра. Зато здесь иллюстрируется переопределение макроса. Мы создали новый макрос с тем же именем. Он заменил собой старый макрос. Те обращения, которые использовали вариант без параметра, не могут применяться после объявления нового макроса.

Рассмотрим пример посложнее, где потребуется два параметра. Пусть нужно изобразить нечто похожее на кристаллическую решетку, где два элемента чередуются в шахматном порядке. Для дополнительной выразительности раскрасим элементы в красный и синий цвет. Сначала делаем прототип, где элементами являются натрий и хлор:

{}_(x.8,H)$color(red)Na$color()_(x1.4,H)$color(blue)Cl$color()_(x.8,H)"";
""_(x.8,H)$color(blue)Cl$color()_(x1.4,H)$color(red)Na$color()_(x.8,H)"";
""|h#-8|h#-4|h"";""|h#-9|h#-5|h""
Описание вышло довольно громоздким. Но тем больше причин для того, чтобы его автоматизировать для дальнейшего использования. Превратим прототип в макрос, заменив Na и Cl на параметры. Назовём их a и b. А сам макрос пусть называется Cross.
@:Cross(a,b){}_(x.8,H)$color(red)&a$color()_(x1.4,H)$color(blue)&b$color()_(x.8,H)"";
""_(x.8,H)$color(blue)&b$color()_(x1.4,H)$color(red)&a$color()_(x.8,H)"";
""|h#-8|h#-4|h"";""|h#-9|h#-5|h""@;
{Cross demo:}_(x2,h,N0)""@Cross(Na,Cl)
Зато теперь мы получили довольно мощную конструкцию:
@Cross(K,Br) @Cross(Li,F) @Cross(Ga,As)

Позиционные и именованные параметры

Допустим мы хотим создать макрос, определяющий производные бензола. Для этого в узлах расставим параметры с номерами, соответствующими номенклатуре IUPAC.
@:Ph(n1,n2,n3,n4,n5,n6)&n1\\&n2|&n3`//&n4`\&n5`||&n6/@()
Хотя мы объявили целых 6 параметров, но для первой демонстрации мы не использовали ни одного. В результате получился просто бензол. Зато теперь мы можем из этой конструкции сделать толуол, указав в первой позиции метильную группу.
@Ph(CH3|)
Но это лишь малая часть того, как можно использовать полученный макрос. Изобразим орто-, мета- и пара-крезол, подключив гидроксильную группу в позиции 2, 3 и 4 соответственно:
@Ph(CH3|,</OH>) @Ph(CH3|,,<\OH>) @Ph(CH3|,,,<|OH>)
Здесь мы использовали позиционную подстановку, пропуская те узлы, которые не нужно заполнять. Но можно сразу указать название того параметра, который нужен:
@Ph(CH3|,n6:<`\HO>) @Ph(CH3|,n5:<`/HO>) @Ph(CH3|,n4:<|HO>) @Ph(n6:<`\H3C>,n2:</CH3>,n4:<|CH3>)

Для первых трех молекул первый параметр задан позиционно. Потому что он и так первый, зачем писать лишнее? А второй параметр указан по названию.
В последней молекуле все параметры указаны по имени. Как видно, в ней 6-й параметр указан раньше 2-го. Потому что не имеет значения, в каком порядке перечислены именованные параметры.

Следует уточнить, что при описании макроса мы получили некоторую особенность, связанную с использованием параметров. Первый параметр является началом цепочки, а остальные должны образовывать ответвления. Поэтому конструкции < > используются для всех параметров, кроме первого. Впрочем, мы можем описать пиридин, указав азот в 4-м параметре. В этом случае ветка не нужна:

@Ph(n4:N)

Использование значений по-умолчанию

Дальнейшим улучшением описания макроса служит использование параметров с заранее заданными значениями (значениями по-умолчанию). Это позволяет описать достаточно сложные структуры с удобным способом внесения небольших изменений. В качестве примера выполним усовершенствование описанного выше макроса Cross. Дадим пользователю возможность влиять на цвет элементов и длину связей. Для этого добавим параметры colorA и colorB, которым через знак : заданы желаемые значения red и blue. Длину центральной связи зададим параметром len1:1.4, а боковой связи - len2:0.8.
@:Cross(a,b,colorA:red,colorB:blue,len1:1.4,len2:0.8)#
{}_(x&len2,H)$itemColor(&colorA)&a$itemColor()_(x&len1,H)$itemColor(&colorB)&b$itemColor()_(x&len2,H)"";
""_(x&len2,H)$itemColor(&colorB)&b$itemColor()_(x&len1,H)$itemColor(&colorA)&a$itemColor()_(x&len2,H)"";
""_(y&len2,H)#-8_(y&len1,H)#-4_(y&len2,H)"";
""_(y&len2,H)#-9_(H)#-5_(y&len2,H)""@;
@Cross(Na,Cl)
Обратите внимание, что после произведенного улучшения осталась возможность использовать макрос точно так же, как и раньше. Это гарантирует, что вся ранее проделанная работа, где содержится обращение к макросу, не потребует переделки. Зато можно пользоваться новыми возможностями там, где это потребуется:
@Cross(K,Br,colorB:darkmagenta,len1:2,len2:1.5)
$color(blue)@Cross(Fe,Cl,len2:1,colorA:black,colorB:darkgreen)$color()
$color(#888)@Cross({[+]},{[-]},len2:0.1,colorA:,colorB:)$color()

Первая структура демонстрирует расширенные возможности использования макроса при помощи дополнительных параметров.
Вторая структура использует внешнее описание цвета линий.
Третья структура демонстрирует возможность отменить те значения по-умолчанию, которые установлены при описании макроса. То есть, вместо $itemColor(red) будет использоваться $itemColor(), что не повлияет на цвет, заданный ранее.

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

@:AminoAcid(r:{R},a:H-,c:OH,rcol:blue)&a$C()N<`|H&>-C<`|H><|$color(&rcol)&r$color()>-C<`||O>-&c@()
Мы получили мощный инструмент, которым можно легко оперировать. (Хотя конечно под эту схему не подходит пролин).
Нарисуем несколько аминокислот: аланин, метионин и глутамин:
@AminoAcid(CH3) @AminoAcid(CH2|CH2|S|CH3) @AminoAcid(CH2|CH2|C`/O/\NH2)
Теперь изобразим для этих молекул реакцию с образованием пептидной связи.
@AminoAcid(CH3) + @AminoAcid(CH2|CH2|S|CH3) + @AminoAcid(CH2|CH2|C`/O/\NH2) -> @AminoAcid(CH3,c:)@AminoAcid(CH2|CH2|S|CH3,a:,c:)@AminoAcid(CH2|CH2|C`/O/\NH2,a:) + 2H2O

Вложенное использование макросов

Из последнего примера видно, что неплохо бы ввести отдельное описание для аминокислот, чтобы не перечислять каждый раз их радикалы в параметрах. Опишем макросы для трёх упоминавшихся выше аминокислот.
@:Ala(a,c)@AminoAcid(CH3,&a,&c)@;
@:Met(a,c)@AminoAcid(CH2|CH2|S|CH3,&a,&c)@;
@:Gln(a,c)@AminoAcid(CH2|CH2|C`/O/\NH2,&a,&c)@;
И перепишем уравнение с образованием пептидной связи
@Ala() + @Met() + @Gln() -> @Ala(c:# )@Met(# ,# )@Gln(a:# )
Тут пришлось явно указать, что в местах стыковки аминогруппа и карбоксильная группа задана пустым местом. Для этого используется #(пробел). Это компромиссное решение, связанное с конкретным способом объявления макроса. Чтобы получить описание @Ala, аналогичное по поведению @AminoAcid, нужно было бы использовать более сложную запись:
@:Ala(a:H-,c:OH)@AminoAcid(CH3,a:&a,c:&c)@;
@Ala() -> {R1}-@Ala(a:,c:){R2}
Здесь всё в руках автора макроса. Он может выбирать, где какой способ применить.:

Попробуем совместить разработанные макросы @AminoAcid и @Ph для описания ароматических аминокислот: фенилаланин и тирозин. У обеих метильная группа стыкуется к узлу 1, а у тирозина гидроксильная группа - к узлу 4.

@:Phe(a,c)@AminoAcid(@Ph(CH2|),&a,&c)@()
+ @:Tyr(a,c)@AminoAcid(@Ph(CH2|,n4:<|OH>),&a,&c)@() =
@Phe(c:# )@Tyr(a:# ) + H2O

Ну и ещё один пример, где используем вложенный вызов @Cross для создания увеличенной структуры.

@:Cross1(a,b,colorA:red,colorB:blue)@Cross(&a,&b,colorA:&colorA,colorB:&colorB,len1:2,len2:1)@;
@:Cross2(a,b,colorA:red,colorB:blue)@Cross1(&a,&b,colorA:&colorA,colorB:&colorB)_(x1,y-3,N0)# @Cross1(&a,&b,colorA:&colorA,colorB:&colorB)@;
@:Cross4(a,b,colorA:red,colorB:blue)@Cross2(&a,&b,colorA:&colorA,colorB:&colorB)_(x-7,y1,N0)# @Cross2(&a,&b,colorA:&colorA,colorB:&colorB)@;
@Cross1(Na,Cl) -> @Cross2(Na,Cl,colorB:green) -> @Cross4(Na,Cl)

Заключение

Подведём итоги. Применение макроопределений позволяет:
  • сократить объём описания для формул с однотипным содержанием;
  • сократить сложность описания за счет декомпозиции;
  • избежать опечаток, появляющихся в процессе дублирования схожей по составу информации;
  • проводить улучшения внутри макроопределений, которые будут автоматически распространяться на остальную часть текста.
Обратной стороной медали является дополнительное время, связанное с изучением данного раздела. Кроме того, описание макроса потребует несколько больше усилий и времени, чем описание формулы. Чем более универсальный функционал, тем больше потребуется время на его тестирование и отладку.