NoITAB – A Stack
ABAP’s internal tables are dynamic data objects that allow to process large quantities of data with the same structure, thereby taking care of dynamic memory management. So they are good at storing database table data and this makes them ubiquitous.
There seems to be no alternative. Complex data structure are usually mapped to internal table to avoid additional effort to implement a special data structure that will probably have poor performance anyway.
But I see a problem here: efficient algorithms often use a dedicated data structure that helps describe design concepts in simple but correct language. Without alternative, the communication and the design discussion suffer. Expressing a problem in a well understood data structure would help reduce the intellectual challenge of programming.
With this in mind, my aim is to showcase ABAP solutions that do not rely on internal tables. Given a problem to solve, the game will be to look beyond the obvious internal table structure, going back to the basics if needed, and find a design that works and is worth the additional effort.
I call this effort NoITAB, pronounced not only internal tables, as in NoSQL that is not only SQL.
A stack is an abstract data type providing access to a Last In First Out collection using 2 operations, PUSH( ) and POP( ).
You will use a stack (or recursion) to fill a tree-like data structure like the ABAP tree view control. In ABAP, prefer a custom stack because the call stack implicitly used by recursion is more limited than the heap.
A stack is really not difficult to implement. Not at all. The enjoy transactions for purchasing documents (ME53N, ME23N and others) manage screens and subscreens using a stack in include LMEVIEWSF01:
FORM push_call_view. APPEND call_view TO call_view_stack. ENDFORM. FORM pop_call_view. IF NOT call_view_stack IS INITIAL. DATA: last TYPE sy-tabix. DESCRIBE TABLE call_view_stack LINES last. READ TABLE call_view_stack INTO call_view INDEX last. DELETE call_view_stack INDEX last. ENDIF. ENDFORM.
- The PUSH( ) operation is translated to an APPEND to add a view at the end of the internal table.
- The POP( ) operation reads the last entry in the internal table and then delete this entry from the table.
If you feel like me, that the POP( ) implementation is not expressing its intent naturally in terms of internal table operations. That may tempt you to strike a new path and DIY – A NoITAB stack implementation using a linked list where we only insert and delete at the top:
DEFINE new_stack. CLASS &1 DEFINITION. PUBLIC SECTION. METHODS push IMPORTING iv_key TYPE &2. METHODS pop RETURNING VALUE(rv_key) TYPE &2. METHODS empty RETURNING VALUE(rv_flag) TYPE xsdboolean. PROTECTED SECTION. TYPES: BEGIN OF ts_node, key TYPE &2, next TYPE REF TO data, END OF ts_node. DATA mr_top TYPE REF TO ts_node. ENDCLASS. CLASS &1 IMPLEMENTATION. METHOD push. mr_top = NEW ts_node( key = iv_key next = mr_top ). ENDMETHOD. METHOD pop. CLEAR rv_key. CHECK empty( ) EQ abap_false. rv_key = mr_top->key. mr_top ?= mr_top->next. ENDMETHOD. METHOD empty. rv_flag = xsdbool( mr_top IS NOT BOUND ). ENDMETHOD. ENDCLASS. END-OF-DEFINITION.
- Is it easy to use?
The generic implementation as a macro makes it flexible and lightweight. Parameter &2 in the macro defines the type of the elements in the stack. With this, I can create a stack of any type, i.e. a stack of strings:
new_stack lcl_stack_string string. START-OF-SELECTION. WRITE: / 'Stack of strings'. DATA(lo_stack2) = NEW lcl_stack_string( ). lo_stack2->push( `Hello World` ). WRITE: lo_stack2->pop( ), lo_stack2->pop( ).
- Does this scale?
The implementation has fixed costs for PUSH( ) and POP( ) operation no matter the number of elements in the stack, i.e. in theory the best possible behavior. The only issue I could think of is the garbage collector that would kick off at unexpected times to reclaim freed memory, which could hurt performance.
I have used this stack often and never bother to measure performance. If you do, please report here.
There is nothing wrong with internal table, but in some cases, we know about better data structures for our collection of data. The simplest case is a stack, where a linked list implementation is efficient and easy to use.
Compared to an internal table, a custom linked list is efficient when we only insert/delete at the beginning or at the end of the list. A custom dynamic array can be efficient if it is fixed-sized, so we can use the ASSIGN with INCREMENT and RANGE command (or the obsolete DO VARYING / WHILE VARY commands) for random access.
Trying to generalize, we could provide functional interfaces to more abstract data types. I like to use an internal table base iterator object to handle the selected entries of an ALV Grid. The benefit here is not performance, but abstraction (Not Only ITAB). I have proposed the same approach for a priority queue.
So I would like to hear about your NoITAB solution.
Keep the Joy of Coding
Maybe it's some fancy 7.4 stuff or I'm just stupid but I didn't get it, sorry. Where is the "macro"? And where is "we only insert and delete at the end"? Why it's "mr_top" and what happened to "mrs_top"? How would you call these methods in a program, for example? And what for / in what context? Not sure I can imagine a business case where this would be needed... Maybe I just don't deal with that kind of stuff, dunno.
And why didn't you measure the performance yourself? That's kind of important. If I wanted the readers to adopt my methods I would've provided some clear arguments.
thank you for your feedback.
I would not look for new business cases where NoITAB would be required, the proposition is to recognize concepts hidden in existing code, extract those and re-implement them in a well-known alternative form.
I have named populating the the tree viewer as an example for stack usage and fetching the selected entries in an ALV Grid as an example for iterator usage. You have to implement those concepts anyway, without naming it. As ABAP code is so dependent on internal tables, the developer might not pause, reflect, and use more expressive data structure even in the appropriate use case.
mr_top stands for object attribute that is a data reference as defined in the extended naming conventions of the Code Inspector. I like this hungarian notation and I think english notation only works for talented technical writers.
Please check the updated the blog for your other remarks, here is the pre 7.4 code
I find it interesting that a person like you, who seems to have a lot of experience with other non-ABAP object oriented programming languages, would be in favor of the hungarian notation.
I have stopped using this notation a few years ago already, without any feeling of loss in readability or functionality, but it's always a good time to challenge our opinions.
my theory is, the people arguing against hugarian notation are good technical writers who feel at ease with the language and chose sensible names for their variables. The prefix is a treat to their creativity, so they complain the loudest.
For most however, the burden of inventing good names is aleviated: lt_ebeln, it_ebeln, rt_ebeln, just use the SAP field name and at the correct prefix.
I don’t believe all those developers of other languages are technical writers. ?
I personally find purchase_documents much better than lt_ebeln and I’m anything, but no technical writer.
Robert C. Martin dedicated a whole chapter in his book Clean Code to naming convention and also did not recommend HN for various reasons. Anyway, I think the recommendations from the official Abap programming guidelines are quite good.
Finally, I think it’s more a matter of taste or what you’re used to.
Best regards, Tapio
"With respect to the method parameters, we identified the prefixes i_, e_, c_, and r_ for importing, exporting, changing, and returning parameters as possible characteristics for distinguishing from data objects declared in the method. Apart from this, no further technical information needs to be expressed with additional prefixes."
I am pushing for this at my company. I'm not going to make prefixes forbidden, not to cause any heart attacks, but I plan on gradually phasing them out.
The macro is between the statements “DEFINE” and “END-OF-DEFINITION”.
I guess this is like "template" in C++. Very interesting, I hadn't seen macros used in this way in ABAP yet. I'll try to keep it in mind.