Butterfly pattern in ABAP
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.
n | 2n-1 |
3 | 5 |
4 | 7 |
5 | 9 |
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
Nice blog with a nice solution. Hope to find my time to think about an own solution to it.
One thing I have to add.
I never felt like a "special" developer. This is just what you do about it. With ABAP you can solve every problem you want to.. that is exact the same which all other languages do out there.
Solving problems 😉
I was talking about perception. Traditionally ABAPers were perceived as a business analysts who has learned "some" coding. Talking about 90s and early 2000. Thanks to SCN, now i do see people manipulating bitmaps, creating ray tracers and interfacing with hardware in ABAP. Plus SAP has added a lot of features to ABAP over these years.
Of course in the end it is all about solving problems!
Hi, Rashid! Have you ever heard about practical use of the butterfly pattern in business applications or it's merely just problem for candidate’s intervew?
It’s a common homework question on programming courses.
Just as a matter of interest, I regularly see the question “how do I write a program in ABAP to display a butterfly pattern” posted by trainees – and reject it, on the grounds that someone doing your homework for you means you will never learn.
My fear is that such folk will find this blog and just copy paste the answer. My hope is that their instructors are more intelligent than them, and will fail them for plagiarism/cheating.
Well, wouldn't it be unfair! I mean people getting such homework assignments in Java or C++ can google and find numerous examples, why not ABAP folks 🙂
Also if the purpose is to discourage copy/pasting, may be teachers can be more creative with assignment like print only right side of butterfly pattern or print butterfly pattern with just one characters like * . ......
You’re right. It’s unfair to C++ and Java programmers if their ABAP colleagues have to work it out for themselves, and in the process gain valuable learning experiences and become better programmers (and indeed better human beings).
I like your blog, but I feel it's let down by one thing - you're using obsolete ABAP elements like PERFORM. That makes me sad.
Sorry this make you sad. But in my defense I was assuming that only following version of perform was obsolete
PERFORM subr(prog) [IF FOUND]
Never knew that the statement itself is obsolete. 🙂
Well, now you do. And here's why.
https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenabap_subroutines.htm
You could make me happy by rewriting your example in a modern OO style.
Happy Now!
Definitely happier. Thanks. 😀
Thank you for a great explanation. You didn't just show t he code, you explained why/what you were doing.
Interesting stuff .
reminded triangle programs of C 🙂
Thanks,
Shivam
Not to rain on anyone's parade and personally, I'm just not into this kind of academic exercise. (Didn't finish Human Resource Machine for the same reason - like what's the business case? 🙂 )
And I have no beef with PERFORM or Hungarian notation (surprised no one complained about it so far, I guess pitchforks are weighing them down 😉 ). But the comment "do not enter value larger than 38" and no actual check of the parameter's value - that's where things can really go wrong in the real life.
Good job on the blog though.
Thanks for your valuable input. Error handling or value checks were not done because it was more about explaining the algorithm, that’s why code not directly relevant to calculating pattern was removed.
Also you can provide input greater than 38. Pattern calculation is not the issue. But for large value, the output formatting got all mixed up.
I like the exercise of using ABAP to solve little programming exercises that the language is not designed or known for. Also, great way to explain how you came to your solution, that really is the more important part of the answer to any of these kinds of questions.
However, if I were to give this as an interview question and you would come with that solution, I would probably not hire you. First off, your solution does not solve the exercise. Using zeroes instead of blanks is the first deviation. Second is the addition of color. If you cannot follow simple and clear requirements, I would not want you on my team.
Another problem I have with your solution is the complexity. ABAP was created initially to generate reports and all you are really doing here is writing a report. There are no complicated calculations, you are just outputting some numbers. Why use a class? Why fill an internal table? And not optimize it for the reads you are doing? So, if anybody does plagiarize your program during a real interview process, they'd better have answers to those questions 🙂
Wow, who could have thought that writing a blog would limit my employment options. 🙂
Thanks for saying this!
edit: updated code as per Rashid’s comment
Very interesting, here is my version (this problem seemed like a good excuse to use the REDUCE expression)
.
First of all, thanks for sharing your code. I never thought REDUCE was that powerful. Thats an excellent example.
Second, for issue with write, you can hold the result in a string and later display that string using CL_DEMO_OUTPUT as below. It works but for number greater than 10, the formatting is out.
Never used the cl_demo_output class before. I guess this execise is also a good use to give that one a try. 🙂
I've updated the code in my original post (format also doesn't break so easily)
Daniel Gent - Niceeeeeee! I like it when people use functional ABAP 🙂
Now that we are at it, you could use CL_DEMO_INPUT instead of the input PARAMETER.
BR, Suhas
Nice blog. Happy to see so many replies in such a short time. Congratulations.
I would definitely introduce this exercise not in a interview but in a training session for starters.
ABAP is a "business" programming language, which means that in real world this kind of scenario is not very likely to arise, but this exercise can help to identify those individuals with an eye for detail and logical thinking.
I'm a big advocate for ABAP OO but would not use it for this exercise. Simply because it is overkill. People need to know which tool to use in each situation. You don't just use a bazooka to kill a fly. 🙂
Finally I present you my solution: 🙂
Finally someone who gets it! No disrespect to functional ABAP but I got a brain freeze looking at the REDUCE option above. As an science experiment - yes. As a productive code - no. Sorry.
Big hug!