Комментарии по теме

«Тра­нза­кция SM02: сообщения в SAP системе»
Олег Башкатов:
С помощью ФМ TH_POPUP можно отправить сообщение конкретному пользователю :-)
«Тра­нспо­ртная система SAP для чайников»
Вячеслав Шиболов:
Хорошая метафора с коробками. Наглядная.
«Вызов тра­нза­кции SAP из писем в MS Outlook»
Олег Точенюк:
Из ABAP для работы с фронт-эндом можно воспользоваться классом CL_GUI_FRONTEND_SERVICES, там есть методы по работе с реестром виндовс.

База знаний

ABAP в XXI веке (часть 2)

2330

Ключевое понятие

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

   

Если вас заинтересовала первая часть обзора функций языка ABAP 7.40, вам непременно стоит прочитать и вторую часть. Эта часть посвящена внутренним таблицам. Этот гибкий и мощный инструмент относится к самым основам ABAP, без внутренних таблиц язык стал бы бесполезным.

К сожалению, структуры ABAP для создания таблиц и обращения к ним до сих пор проходят эволюционный этап операторов «MOVE x TO y» и «COMPUTE X = y + 1», о которых шла речь в первой части: Они избыточны и неортогональны. Применяя их, мы думаем о микровоздействии на состояние, а не о наборах команд для создания значений на основе других значений, что было бы целесообразно для значительной доли задач программирования.

При работе с таблицами приходится сталкиваться с такими операторами:

READ TABLE tab WITH KEY c1 = ’a’ ASSIGNING <line>.

LOOP AT tab1 ASSIGNING <line> WHERE c1 = ’a’.

INSERT <line>-c2 INTO TABLE tab2.

ENDLOOP.

Вспомним, как задания (преобразование типа и создание структурированных значений) были вызволены из императивного мрака выражениями конструктора:

use_string( CONV string( char10 ) ).

use_struct( VALUE t_struc( c1 = ’a’ c2 = 10 ) ).

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

READ TABLE tab INDEX 1 ASSIGNING FIELD-SYMBOL(<line>).

INSERT VALUE #( c1 = 'a' c2 = 10 ) INTO TABLE tab.

Мы уже знаем, как создавать таблицы с фиксированным числом строк в виде значений:

use_inttab( VALUE #( ( x - 1 ) ( x ) ( x + 1 ) ) ).

Нам необходимо рассмотреть два важных момента:

  • Доступ к табличным строкам без операторов для манипуляций с состоянием, например, READ.
  • Создание табличных значений с переменным количеством строк (например, на основе строк другой таблицы).

Далее мы перейдем к более сложным задачам: сокращение таблицы до нетабличного значения, обработка табличных строк в группах (вспомните GROUP BY для SQL) и выполнение с таблицами старой доброй операции MOVE-CORRESPONDING с пользовательским мэппингом.

Выбор табличных строк

Как известно, только один вариант синтаксиса для обращения к таблице не сбивает с толку 90% программистов: квадратные скобки. Для обращения к первой строке таблицы следует написать

tab[ 1 ]

Да, это новый синтаксис ABAP для «READ TABLE tab INDEX 1».

Этот новый вид выражений работает во всех позициях грамматики ABAP с применением выражений (которая сводится к операндам чтения неустаревших непериферийных операторов). Но выбранная строка таблицы также является l-значением, сопоставимым в этом отношении с выражениями NEW и CAST, описанными в первой части:

tab[ 1 ] = struc.

tab[ 2 ]-c1 = 'a'.

Первая строка является присвоением, которое изменяет выбранную строку таблицы в целом, а вторая строка показывает изменение только одного компонента строки путем сцепления выбранной по номеру строки таблицы с компонентом через символ дефиса «–» и последующим именем поля таблицы.

В «[…]» доступны два способа выбора: по индексу или по значению(ям) компонента. Для второго способа применяется следующий формат:

tab[ c1 = x c2 = f( y ) ]

Если критерию поиска соответствуют несколько строк, выбор строки определяется порядком вставки (аналогично для READ). Оба варианта могут относиться к явному ключу, определенному для типа таблицы:

tab[ KEY key1 INDEX 1 ]

tab[ KEY key1 c1 = x c2 = f( y ) ]

Первичный или вторичный ключ, используемый для доступа к индексу, не должен являться хеш-ключом. Компоненты явного ключа должны совпадать, например, как в операторе «READ TABLE … WITH TABLE KEY». В противном случае выражение получает семантику произвольного ключа «READ TABLE … WITH KEY» с имплицитным использованием первичного ключа по возможности. Компоненты, как и имя ключа, могут быть динамическими:

tab[ KEY (keyname) c1 = x (compname) = y ]

Таким образом, для большинства разновидностей оператора READ существуют функциональные эквиваленты. (Варианты «FROM workarea» и «BINARY SEARCH» не поддерживаются.)

У разработчика на ABAP возникает естественный вопрос: Это READ … INTO или READ … ASSIGNING (т.е. я получаю копию строки или указатель на нее)?” Ответ: Обычно это неважно, компилятор учитывает контекст и тип строки:

  • Семантика указателя задается определенным контекстом (например, вызов метода, см. ниже).
  • Кроме того, если строка относится к родовому типу, указатель становится единственной опцией.
  • Если тип строки широкий или глубокий, компилятор выбирает семантику указателя, поскольку в данном случае это эффективнее. Если тип строки узкий и плоский, вы получаете семантику копии. Такая оптимизация применима в большинстве позиций значения r.

В вызове метода компилятор использует семантику указателя по умолчанию. (Семантика копии как неявная оптимизация может привести к непредвиденному поведению в результате побочных эффектов.) Однако если тип строки узкий и плоский, Extended Syntax Check (SLIN) выдает предупреждение с рекомендацией выбрать VALUE. Эта рекомендация подразумевает следующий вариант синтаксиса:

VALUE #( tab[ … ] )

Для выбора таблицы строка копируется из таблицы в отдельное значение. Как правило, этой рекомендации SLIN стоит последовать. Аналогичный вариант

REF #( tab[ … ] )

получает ссылку на нужную строку (например, если требуется параметр типа строки REF TO).

Это новый внутренний синтаксис для операторов VALUE и REF из первой части публикации. Дополнительного оператора для ASSIGNING не предусмотрено; это значение по умолчанию. Но как явно присвоить строку таблицы символу поля, возможно, совмещенному с линейный описанием? Например:

ASSIGN tab[ i ] TO FIELD-SYMBOL(<line>).

Выше я уже приводил пример сцепки компонентов. Выполняется обобщение по структурам данных с таблицами, вложенными на произвольную глубину. Для типов «таблица таблиц» цепочка выглядит как обращение к многомерному массиву:

ASSIGN matrix[ x ][ y ] TO <point>.

Либо цепочка включает в себя тире и имя компонента («-tab1», «-c3»):

tab0[ x ]-tab1[ c1 = y c2 = z ]-c3

Обратите внимание на сходство синтаксиса с цепочками для вызова метода:

meth0( x )->meth1( p1 = y p2 = z )->a

Вызов метода может находиться после, но не перед выбором таблицы (это приводит к появлению неэффективных структур, которые с помощью вызова метода извлекают всю таблицу вместо извлечения одной строки):

t1[ x ]-reftab[ y ]->meth( )

t2[ x ][ y ]-ref->meth( )->a

«meth( )[ x ] « не разрешено, это синтаксическая ошибка

Примеры цепочек наглядно показывают, как можно сократить громоздкий императивный код, наполненный вспомогательными переменными:

READ TABLE t1 INDEX x ASSIGNING FIELD-SYMBOL(<x>). « *yawn*

READ TABLE <x>-reftab INDEX y INTO DATA(ref). « zzzz…

ref->meth( ).

А что будет в случае сбоя при выборе? Очевидно, что для такой ситуации не предусмотрен код ошибки в SY-SUBRC. Побочные эффекты выражений – вещь крайне неприятная. Концепция выражения никогда не изменяет поля SY. Вместо этого проблемный выбор таблицы генерирует особую ситуацию класса CX_SY_ITAB_LINE_NOT_FOUND. Объект особой ситуации содержит информацию о предмете сбоя (например, какой из индексов). В ряде случаев, но не всегда, это позволяет выявить проблемный выбор в цепочке. Если требуется усилить контроль, разделите цепочку. Например:

ASSIGN t2[ x ] TO FIELD-SYMBOL(<x>).

CHECK sy-subrc = 0.

ASSIGN <x>[ y ] TO FIELD-SYMBOL(<y>).

В отличие от выбора на уровне выражений оператор ASSIGN, по собственной традиции, устанавливает для SY-SUBRC значение 0 (успех) или 4 (сбой), на которое можно реагировать в потоке управления.

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

VALUE #( tab[ x ][ y ] DEFAULT deflt )

В случае сбоя любого выбора в цепочке выдается значение deflt. Для получения начального значения в данном случае пишите так:

VALUE #( tab[ x ][ y ] OPTIONAL )

Для тестирования наличия строки используется встроенная предикативная функция:

CHECK line_exists( tab[ c1 = x ] ).

Если требуется индекс найденной строки:

DATA(i) = line_index( tab[ c1 = x ] ).

Если строка не найдена, возвращается значение 0. Обе функции используются для выбора в цепочке. В этом случае результат относится к окончательному выбору в цепочке. Ни одна из них не генерирует особые ситуации.

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

tab1[ idx1 ]-a = tab2[ idx2 ]-x.

tab1[ idx1 ]-b = tab2[ idx2 ]-y.

tab1[ idx1 ]-c = tab2[ idx2 ]-z.

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

ASSIGN tab1[ idx1 ] TO FIELD-SYMBOL(<r1>).

ASSIGN tab2[ idx2 ] TO FIELD-SYMBOL(<r2>).

<r1>-a = <r2>-x. <r1>-b = <r2>-y. <r1>-c = <r2>-z.

Генератор таблиц

Мы рассмотрели обращение к табличным строкам. Следующей задачей является создание таблиц в виде значений без обращения к варианту «много мелкий манипуляций с состоянием»:

LOOP AT tab ASSIGNING FIELD-SYMBOL(<x>) WHERE c0 = ’X’.

INSERT VALUE #( d1 = <x>-c1 d2 = <x>-c2 )

INTO TABLE tab1.

ENDLOOP.

Необходимая концепция давно известна и формально уходит корнями в нотацию математических множеств:

{ f (x) | x Î S, P(x) }

Вычисляется ли набор значений f (x) функцией результата f для всех элементов x из исходного набора S, для которых верен предикат P(x). В случае применения к контейнеру списка (распространено во многих функциональных языках, например, в Haskell) это называется генерацией списка. В ABAP роль контейнера играет внутренняя таблица, поэтому название было изменено соответственно. Синтаксически получаемое выражение f (x) перемещается в конец. Таким образом, генерация таблиц становится плавной генерализацией создания таблиц с помощью оператора VALUE (Рис. 1).

  • Условие FOR вводит LOOP на уровне выражения. Оно привязывает локальный символ поля (с именем «<…>») для семантики LOOP ASSIGNING или локальную переменную для семантики LOOP INTO. Как было сказано в первой части, локальную переменную можно повторно использовать в других выражениях, но не уровне оператора. Следует четко разграничивать локальные и нелокальные переменные.
  • Выражение после IN указывает исходную таблицу, строки которой, в свою очередь, привязаны к символу FOR.
  • Дополнительно проверенные стоки можно сократить до подмножества:
  • Условие FROM /TO указывает диапазон индексов и исходной таблице.
  • Условие WHERE фильтрует строки по логическому выражению. (Круглые скобки вокруг логического выражения обязательны, в противном случае возможны проблемы с синтаксическим анализом.)
  • Перед этими условиями может стоять условие USING KEY (не показано) для указания ключа таблицы для этих ограничений.
    • Условие LET для привязки локальных переменных уже знакомо нам по первой части статьи. В генераторе таблиц оно, как правило, используется для предотвращения множественных вычислений значения из текущей строки (для ссылки используется символ FOR). На Рис. 1 обратите внимание на привязку выбора таблицы (l-значение!) к локальному символу поля (не переменной). Это позволяет избежать копирования семантики для табличного поиска.
  • Наконец, далее следует одна или несколько спецификаций строк, как в статическом случае, но с использованием символа FOR.

Вы хотели бы увидеть полную версию статьи?

Если вы являетесь подписчиком журнала SAP Professional Journal, пожалуйста, авторизируйтесь на сайте.

Если вы хотите подписаться на журнала SAP Professional Journal, пожалуйста, обратитесь в редакцию или сделайте заказ на сайте.

Правила получения тестового доступа к статьям SAP Professional Journal


Любое воспроизведение запрещено.
Копирайт © «Издательство ООО «Эксперт РП» Copyright © 2010 Wellesley Information Services. All rights reserved.