Some time ago while browsing the community site, I came across a question where someone asked to output butterfly pattern in ABAP. Normally such problems/questions are common in interviews of other programming languages such as C++ or Java where participants are asked to develop a short program in order to solve a given problem such as generate or perform some operation on a Fibonacci sequence, sort some array or list with some unique conditions or implement binary search etc.
But ABAP!!! I was happy to know that now ABAPers are treated as normal programmers or at least the world has started asking those questions that other programmers are answering for a long time.
The reason for this blog was not to present a solution but rather explain the way this solution was achieved. The solution presented is how I understood and solved the problem. I am sure there will be many other ways to solve this. You are welcome to share your solutions.
The Problem
For a given number (for example 3) out put a pattern that should be as following
Figure 1
As you can see the output matches a butterfly wings (in a symbolic way), hence the name Butterfly pattern.
Solution
Since we are talking about a generic solution for any given number n, it is better to have some more instances of output for different numbers and try to find the pattern there. So let’s have a pattern for 4 and 5 there.
Figure 2
Figure 3
What we can observe is that output is a square matrix that is number of rows and columns in output are equal. That’s first observation.
Then we try to establish a link between the input n and the number of rows/columns in output.So we observe that for input 3, we have 5 rows/columns in output, for input 4 it is 7 and for input 5 it is 9 rows/columns in output.
That solves the rows/columns issue. We have seen from these examples that for any given input n, the number of rows/columns will be 2n-1.
OK, so this concludes we can have two loops (from 1 till 2n-1) where outer one can be for columns and inner one for rows OR vice versa depending on the logic we choose. In this solution, I have chosen to fill columns as this seems easier. If you look at fig 1,2,3 you will notice that values in a column are either space or some number. And if it is a number, it is same for the whole column.
Thus we can have outer loop for columns and inner for rows where we try to fill a whole
For col = 1 till (2n-1)
For row = 1 till (2n-1)
Fill cell(row, col) “Fill either with space or a number
End for row
End for col
Now we have to find out for a given column, what is the logic or formula to print the value and how can we deal with space or blank values.
First the number or value printed in each column.
Referring to fig 1,2,3 we see that first column is always filled with number n , the next one with n-1, next one with n-2 and this goes on until the value reaches 1. Once it reaches 1, it starts incrementing. So the column right afterwards will have a value of 2, the next one will have 3 and next one ………. You get the idea.
The pattern here we can observe is that column value starts from n, it keeps on decreasing until it reaches 1 and then it keep on increasing until it reaches n again. So if we for example look at figure 3 (butterfly pattern for 5), we can the following pattern (just refer to first two columns of table below)
Column |
Value (in column) |
n – Column |
ABS(n – Column) + 1 |
1 |
5 |
5-1 = 4 |
5 |
2 |
4 |
5-2 = 3 |
4 |
3 |
3 |
5-3 = 2 |
3 |
4 |
2 |
5-4 = 1 |
2 |
5 |
1 |
5-5 = 0 |
1 |
6 |
2 |
5-6 = -1 |
2 |
7 |
3 |
5-7 = -2 |
3 |
8 |
4 |
5-8 = -3 |
4 |
9 |
5 |
5-9 = -4 |
5 |
Can we develop some logic here? Looking at the table above, we can see that the difference between column number and value is constant. In this table observe third column. Here we are subtracting column number from the given input n. See the result value fluctuates from 4 till -4. Looking at first five rows of above table we can assume a formula here like
PrintValue = (n – Column) + 1
However this will fail for sixth row as in this case n- column will result in 5-6 = -1. What if we use “Absolute” value function? That can solve the problem. So the above formula can be re-written as
PrintValue = ABS(n – Column) + 1 “ABS will return absolute value for the given argument
This solves the issue of what value to be printed in each column.
So our pseudo code will become something like
For col = 1 till (2n-1)
For row = 1 till (2n-1)
Value in cell(row, col) = ABS(n – col) + 1
End for row
End for col
Now we address the issue of blanks. How to deal with blank values? Again if we look at fig 3, we can see a pattern of blanks here. Since we are trying to fill the columns first in this solution, so let’s analyze the columns and see if there is a pattern for blank spaces. For Column 1, there are no blanks. For column 2 we have two blank values; one at first row and one at last row. Similarly for column 3 we have total 4 blanks with two at top two rows and two at bottom two rows.
Column |
Blanks Top |
Blanks Bottom |
n - PrintValue |
1 |
0 |
0 |
5-5 = 0 |
2 |
1 |
1 |
5-4 = 1 |
3 |
2 |
2 |
5-3 = 2 |
4 |
3 |
3 |
5-2 = 3 |
5 |
4 |
4 |
5-1 = 4 |
6 |
3 |
3 |
5-2 = 3 |
7 |
2 |
2 |
5-3 = 2 |
8 |
1 |
1 |
5-4 = 1 |
9 |
0 |
0 |
5-5 = 0 |
With ref to table above, if we ignore the bottom rows for the time being, we can see a pattern. The sequence of blanks goes from 0 till 4 and then back to 0. This pattern is very much like the pattern we see in “Value” part above where we make use of ABS function to get absolute value. Can we reuse that “Value” here? Seem like we can. Refer to the fourth column in above table. This formula returns the number of blanks (top or bottom) for any given column. Once we have this value; let’s call it pad value, we can use a simple if statement to check if the current row should print blank or “PrintValue”
If statement could be something like
If current row <= “Pad value”
then print blank
else print Value
The above snippet can cover the top part. For bottom part we need to add something like
Bottom row = ( 2n – 1) – current row
If bottom row <= “Pad value”
then print blank
else print Value
That concludes pretty much the whole logic. The ABAP program given below uses an internal table with structure referring to type below. It has two fields to represent row and column. The third field represents the value at that row/column.
TYPES: BEGIN OF tt_tab,
row TYPE i,
col TYPE i,
val TYPE i,
END OF tt_tab.
Program class has two Methods and a Constructor. The constructor just initializes the private variable lv_inp with input parameter p_inp. The first method “Create_pattern” fill the internal table mentioned above with relevant values for butterfly pattern.
The second method “output_pattern” is a quick and dirty way to output the pattern. Obviously more formatted output can be obtained by using different output options such as ALVs, web Dynpros or BSPs (HTML table).
*&---------------------------------------------------------------------*
*& Report YYRJ_TEST0079
*&
*&---------------------------------------------------------------------*
*& Create a butterfly pattern against a given input p_inp.
*& Due to restrictions on list width, please do not enter
*& a value bigger than 38 in p_inp.
*&---------------------------------------------------------------------*
REPORT yyrj_test0079 LINE-SIZE 1023.
PARAMETERS: p_inp TYPE i OBLIGATORY. "input number
CLASS butterfly_pattern DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF tt_tab,
row TYPE i,
col TYPE i,
val TYPE i,
END OF tt_tab.
METHODS:
constructor
IMPORTING p_input TYPE i,
create_pattern,
print_pattern.
PRIVATE SECTION.
DATA: lv_inp TYPE i.
DATA: lt_tab TYPE STANDARD TABLE OF tt_tab,
ls_tab TYPE tt_tab.
ENDCLASS.
CLASS butterfly_pattern IMPLEMENTATION.
METHOD constructor.
lv_inp = p_inp.
ENDMETHOD. "constructor
METHOD create_pattern.
DATA: lv_n TYPE i, "total num of rows or cols in pattern
lv_row TYPE i, "row counter
lv_col TYPE i, "column counter
lv_val TYPE i, "value to be printed
lv_pad TYPE i. "pad counter for each column
lv_n = ( lv_inp * 2 ) - 1.
lv_val = lv_inp.
lv_pad = lv_inp.
DO lv_n TIMES.
lv_col = sy-index.
lv_val = abs( p_inp - lv_col ) + 1.
lv_pad = p_inp - lv_val.
DO lv_n TIMES.
lv_row = sy-index.
CLEAR ls_tab.
ls_tab-row = lv_row.
ls_tab-col = lv_col.
IF ( sy-index <= lv_pad ) OR ( ( lv_n - lv_row ) < lv_pad ).
CLEAR ls_tab-val.
ELSE.
ls_tab-val = lv_val.
ENDIF. "lv_pad check
APPEND ls_tab TO lt_tab.
ENDDO. "lv_n for row
ENDDO. "lv_n for col
ENDMETHOD. "create_pattern
METHOD print_pattern.
DATA: lv_n TYPE i, "total num of rows or cols in pattern
lv_row TYPE i, "row counter
lv_col TYPE i. "column counter
lv_n = ( lv_inp * 2 ) - 1.
DO lv_n TIMES.
lv_row = sy-index.
DO lv_n TIMES.
lv_col = sy-index.
READ TABLE lt_tab INTO ls_tab WITH KEY row = lv_row
col = lv_col.
IF sy-subrc = 0.
IF ls_tab-val IS INITIAL.
WRITE: ls_tab-val.
ELSE.
WRITE: ls_tab-val COLOR 3.
ENDIF. "ls_tab-val
ENDIF. "subrc read table
ENDDO. "lv_n for lv_col loop
SKIP 1.
ENDDO. "lv_n for lv_row loop
ENDMETHOD. "print_pattern
ENDCLASS.
DATA: lo_patt TYPE REF TO butterfly_pattern.
START-OF-SELECTION.
CREATE OBJECT lo_patt EXPORTING p_input = p_inp.
*** create pattern
lo_patt->create_pattern( ).
*** output pattern
lo_patt->print_pattern( ).
Sample output for n = 4
Sample output for n = 9