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.
For a given number (for example 3) out put a pattern that should be as following
As you can see the output matches a butterfly wings (in a symbolic way), hence the name Butterfly pattern.
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.
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