10

Why do we practice printing patterns while coding? Patterns like:

1
1 2
1 2 3
1 2
1

1
2 3
4 5 6
7 8 9 10
11 12 13 14 15, etc.

Are these kinds of pattern questions just for fun and to get better at using loops, or are there any interesting applications of these types of pattern questions? Or any interesting math behind them?

  • 3
    Fast feedback loops! – WBT Mar 30 '22 at 19:09
  • 3
    Depending on the language and the pattern, it may also be to get better at using recursion properly. Assuming I was told to do a parameterized implementation of either of your examples in a language like Elixir (or pretty much any other language with proper TCO) I would actually jump first to a recursive implementation even if looping were possible in the language. – Austin Hemmelgarn Mar 30 '22 at 23:08
  • 1
    Turning a mathematical expression or idea into an algorithm is not always a 1:1 translation. It seems to be a "hard thing", generally, for programmers to adapt mathematics into code. This type of practice helps those skills, and they are important in many fields of applied science and engineering. – J... Apr 01 '22 at 16:01
  • @AustinHemmelgarn These sort of patterns would likely be poor applications of recursion for imperative languages, though, which is the common case. CS educators forcing students to use recursion for O(n^2) loop problems like this causes a lot of hapless students to wind up in [the recursion tag](https://stackoverflow.com/questions/tagged/recursion) on Stack Overflow. – ggorlen Apr 05 '22 at 15:41
  • @ggorlen Agreed, which is why I prefaced my comment with ‘depending on the language’. I would never consider recursion for something like this in a language like C or Python, but would absolutely prefer it to any other looping-equivalent construct in something like Elixir or Erlang. – Austin Hemmelgarn Apr 05 '22 at 16:44
  • @AustinHemmelgarn Yes, you nailed it in the original but I wanted to over-emphasize for posterity. Misapplication of recursion in education is a pet peeve of mine. – ggorlen Apr 05 '22 at 17:10

8 Answers8

31

Why do we practice printing patterns while coding?

Because bugs love to hide in loops.

Talk students into using loops to print these patterns and they will create bugs. Tracking down and squishing bugs in the confusing code this exercise produces is a skill that will last as long as their coding career.

Fail to talk them into using loops and you'll find they very wisely write a bug free print statement for each line of your output. Which solves the problem without learning the lesson. Have a good explanation why this isn't preferred. Understand that you likely wont find a good technical explanation. Sometimes pros do the same thing. It's called loop unrolling.

Don't claim loops are more flexible, efficient, or readable. They aren't. Loops just follow patterns. Sometimes they are a good way to express the pattern. Capturing a pattern in a loop gives you expressive power. It lets you create, manipulate, and display things that follow the pattern.

This is an exercise in pattern recognition, loop construction, and debugging. Pull this off and I consider you a real coder.

are there any interesting applications of these types of pattern questions?

Absolutely. They range from silly toys most often used in job interviews: FizzBuzz

To beefy algorithms used to keep growing networks working: Dijkstra's_algorithm

While they have wildly different applications, both really just manipulate numbers to follow a pattern.

candied_orange
  • 1,063
  • 8
  • 11
  • 10
    "*Don't claim loops are more flexible […]. They aren't.*" - well hardcoding a pattern works well for a *single* pattern. Now make the exercise to print the pattern in an arbitrary size, or to print the first N lines of the infinite pattern… I can't see how you'd do that without the flexibility of a loop (or recursion). – Bergi Mar 30 '22 at 21:11
  • 3
    @Bergi That isn't more flexibility. That's parameterizing. A student with access to copy and paste and the code has the same flexibility and easier to debug code. The trick is getting them to see why changing N is better than copy and paste. The answer is sometimes you don't know N until this code is running on some server at 3am. I could have my boss wake me up with phone call about it but I'd rather sleep through the night. – candied_orange Mar 30 '22 at 21:35
  • 9
    By this argument, no code is more flexible than a collection of special-cases. I'd argue the flexibility provided by loops is the flexibility provided to another individual, who may not have the same knowledge as you do. In many cases, this is a user who provides input, in the form of a file or otherwise. In the case of `std::sort`, it is in the form of a developer who may not have the understanding of sorting algorithms, or may not wish to exercise said understanding. – Cort Ammon Mar 31 '22 at 02:30
  • 1
    If bugs love to hide in loops, then shouldn't students be taught *not* to use them? None of the examples in the question require loops, they can be solved using unfolds, folds, and maps. In fact, I have rarely seen a problem that required a loop or even was made simpler by one. – Jörg W Mittag Mar 31 '22 at 10:03
  • @JörgWMittag writing loops gets students to write bugs. There is no substitute for having to fix your own bug. And no better teacher that already debugged canned loop alternatives are often preferable to writing your own loop. – candied_orange Mar 31 '22 at 11:18
  • That said. Loops aren’t going away. Students should respect them and master them. Doesn’t mean they must prefer them. – candied_orange Mar 31 '22 at 11:31
  • 1
    @candied_orange Loops are basically the reason why you program. Solved problem and need to do it 1000 times more? Wrap it into a loop... done! Without loops you'd constantly repeat yourself and where's the fun in that? Seriously I'm actually astonished people could not like loops. For real, "algorithms" are just breaking up a problem into a series of simpler problems with the intention of letting the computer run those simple problems in a loop. Without loops you wouldn't so much program you'd operate a machine. Seriously I can't wrap my head around how you could not think loops are cool. – haxor789 Mar 31 '22 at 11:38
  • @haxor789 I program to solve problems not write loops. Loops are cool except when I'm tired and want to go home but can't because this custom loop you made me look at still wont behave. If you want to fall in love with something try abstraction. – candied_orange Mar 31 '22 at 12:43
  • @candied_orange As said you basically solve actual real world complex problems by breaking them down into machine processable parts, because machines are faster at doing those things and they are faster because of constructs like loops if you actually have to write down the commands you could almost do it yourself. Sure if you abstract things behind functions and program calls you're able to do a ton of magic, but then you're not teaching computer science you're instructing a user. – haxor789 Mar 31 '22 at 12:52
  • 1
    @haxor789 That way of thinking leads back to writing goto "like a real programmer". No, show me you can do abstraction well and I've got much more respect for you than if you just show me you know how to crank out a loop. – candied_orange Mar 31 '22 at 12:59
  • 3
    @haxor789: The only time I ever need loops is to work around deficiencies in the programming language and/or the library. In most languages I work in, if you're using a loop you're doing it wrong. Some of them don't even have loops. I, personally, find recursion patterns like anamorphisms, catamorphisms, or hylomorphisms much easier to understand and most importantly much harder to get wrong (can't have an off-by-one indexing error if there's no index). If all else fails, there's always tail-recursion. – Jörg W Mittag Mar 31 '22 at 13:14
  • @JörgWMittag completely agree (except, be aware that not every language optimizes tail-recursion). – candied_orange Mar 31 '22 at 13:19
  • 2
    @candied_orange JörgWMittag The point is about the coding patterns that let you avoid repetition, perform an operation stepwise, let's you manipulate datastructures manually if necessary instead of relying on knowledge of the commands of a particular language and stuff like that. Whether you use go to, for/while/do-while/for each or recursions doesn't really matter. Does it even conceptually matter whether the language optimizes tail-recursions? – haxor789 Mar 31 '22 at 13:31
  • @haxor789 it does when you blow the stack – candied_orange Mar 31 '22 at 13:40
  • 1
    @haxor789 If the language doesn't optimize tail-recursions, they're quite limited in the number of iterations they can do. They certainly can't emulate a loop from 1 to a million, for example. – prosfilaes Mar 31 '22 at 13:44
  • @candied_orange Fair enough I had that stackoverflow joke coming. Though that just shows for why functions are not ideal to use as loops as they produce a lot of overhead necessary for functions but unnecessary for the loop part that you're interested in. – haxor789 Mar 31 '22 at 13:44
  • 2
    @haxor789 : ) ok seriously, you seem to think I'm arguing against loops. I'm not. I'm arguing that loops are buggy, hard to read, untrustworthy piles of code that any pro will regard with deep suspicion. They are powerfully expressive and they aren't going away no mater how many abstractions language designers throw at them. So they are a perfect lesson for new programmers to learn. Just give them plenty of time because this stuff aint easy. – candied_orange Mar 31 '22 at 13:51
  • 2
    @candied_orange I mean I get that you argue for loops as a means for learning debugging. But I'd argue against the assertions that they are inherently buggy, hard to read or untrustworthy. They are as safe as their user. Sure if you multiply your coding power with a loop the impact of small bugs is getting amplified as well. But having that power is kinda what you aim for and would bite you with abstraction in pretty much the same way. Not to mention that you can always unroll a loop whereas rolling up a loop, so to say, is an actual challenge. And of course grasping a few lines is easier. – haxor789 Apr 01 '22 at 12:11
  • I must protest against you teaching a new generation of developers to think of bugs as mischievous actors who inevitably creep into our code when we aren't looking and "hide in loops." If your code does not make the machine behave as you expected it to behave, then your code is _defective._ It's long past time for software developers to acknowledge that when code contains a defect—no matter how subtle—it always was the programmer (or team of programmers) who created it and, who bear responsibility for finding it and fixing it. – Solomon Slow Apr 01 '22 at 12:29
  • @SolomonSlow sigh. The point of the lesson is to experience the bugs: creating them, finding them, and fixing them. They do inevitably creep into our expressions. Note how many times I had to edit my answer. Heck I even deleted and reposted some of my comments. Call them defects, bugs (See Grace Hopper), or typos; reality often falls short of the ideal. This isn't even a new idea. Plato argued that any form of triangle we experience is an imperfect form of the ideal triangle. So we should expect the bugs. Or as my 11th grade English teacher put it: "Did you read this before you gave it to me?" – candied_orange Apr 01 '22 at 14:49
  • You're completely missing my point. But I guess that's on purpose, so I won't argue it any further. – Solomon Slow Apr 01 '22 at 15:03
  • If I'm missing something I'm not doing it willfully. If someone is willing to make me see it I'm willing to listen. Look at my profile to see how I approach this. – candied_orange Apr 01 '22 at 15:16
  • OK, That's fair. My point is, I can divide developers with whom I have worked into two groups; those who said "bug," and those who said "defect." The ones who said "bug" tended to create more software defects and, tended to show less concern for them than the ones who said, "defect." An attitude—not a common attitude, but one that was exclusive to the "bugs" crowd—said, "It's not my problem, until the testing department reports it." (Same crowd also was more likely to look down on the testers, or even to consider them as adversaries.) In short: the "defect" people were better team players. – Solomon Slow Apr 01 '22 at 16:45
  • @SolomonSlow I had thought you wanted me to back up my thesis that loops are hard to read. Which I can if you like. But now it seems you want me to choose to use stuffy formal language to express myself because it will somehow make me a better coder. I really don't know where that idea came from. In my experience everyone says bug and defect and choses which to use based mostly on who they are talking to. Students certainly should know both terms. But if you have some actual data or study that backs up your claim I'd love to see it. – candied_orange Apr 01 '22 at 17:02
  • 1
    Re, "don't know where that idea came from." It came from my personal experience—40+ years of writing code. You said, "bugs love to hide..." OK, that sounds more fun than saying "its really hard to write defect-free code." But, when you personify bugs like that, IMO, it sounds like an invitation to shift the blame. In my experience, developers who personify bugs have more of a lax attitude toward them. It's as if, at _some_ level, when they say, "Weird! I don't know how that bug got in there" instead of saying, "Damn! I sure ***ed that up," they're denying their culpability for the problem. – Solomon Slow Apr 01 '22 at 17:35
  • 1
    @SolomonSlow I'm not sure that translates to every team. On my team, the people who say "bug" are more likely to be interested in making the software work right no matter how things happened. The people who say "defect" are more interested in figuring out how to assign blame. – user3067860 Apr 01 '22 at 20:29
9

Are these kinds of pattern questions just for fun and to get better at using loops [...] ?

Absolutely yes. Practical applications of such patterns nearly don't exist. It's good training for control constructs like loops, and you can immediately see whether you got it right.

Ralf Kleberhoff
  • 499
  • 2
  • 4
  • 6
    Practical applications of algorithms that can generate such patterns do exist and are very common. They include: generating node traversal sequences in pathfinding or graph crawling algorithms, generating brute force password sequences, similar to the brute force, generating permutations or combinations to solve word problems (eg, Wordle solver, Scrabble solver or the first part of decoding an encrypted message if you have a rough idea of some of the content of the message) etc.. – slebetman Mar 31 '22 at 03:02
  • 4
    I almost applied for a job once at a company building sewing machines with lots of very clever software - yes, I might have written lots of code to create patterns. Seriously :-) – gnasher729 Mar 31 '22 at 10:01
  • The `tree` command is another nice example of loops, recursion and printing. It is great visual tool for understanding the structure of directories and their contents. – Fritz Sieker Apr 02 '22 at 15:35
6

I was skeptical about this as well. Those pictures feel like a cheesy attempt to make programming cool, from an era when that meant ASCII graphics; or the sort of cutsie junk we need to keep the attention of non-majors. But I've come around to realize they're nice teaching examples of nested loops, at a few levels.

The way nested loops work isn't obvious. Showing how for i=1 to 8; { for j=1 to 5; print "*" } actually prints 40 stars (and not 13) is helpful. Adding the newline for that 8x5 box is a nice way to visualize how the loops run.

Making a silly right triangle with for i=0 to 7; for j=0 to i introduces the idea of using the outer loop variable to influence the inner loop. That's an idea we'll need later for O($n^2$) selection and insertion sorts. Different patterns allow us to use i in different places j=i to WIDTH (right-triangle on right edge) or even both sides -- j=i to WIDTH-i (pyramid).

More complex things, pyramids or X's or a hollow square, are nice practice with 0-based index math -- how i runs from 0 to HEIGHT-1 and how to plug that into j<HEIGHT-i to reason about the length of the 1st and last row. Off-by-one errors are very easy to spot in these pictures.

All of this is nice for ComSci majors who'll need to be good with this stuff for their algorithms classes. But even for an aspiring web developer in a 2-year program, these are a quick warm-up for creating a table, before adding all of the mark-up.

Coding note: the examples change a little for console vs. graphical system, but not the essence. If printed on a simple console, sometimes the inner loop must run 0 to WIDTH, with the interesting part inside. Ex: if(i<WIDTH-j) ch=" "; else ch="*";. Whereas with a graphical system, the inner loop can do the work: j=WIDTH-i to WIDTH; colorBox(i,j). Or on a console we can fake that by "printing" into a 2D array which a function displays later.

Owen Reynolds
  • 251
  • 1
  • 6
  • It's not really "cool designs" from way back. It's to get non-programmers their first experience with looping. - I taught CS. – Rick Henderson Mar 31 '22 at 19:26
  • 1
    @RickHenderson They are from way back -- books of BASIC programs from the 1980's had them. But thanks for the "for non-majors" idea. That aligns with my 1st para that it's easy to assume these are cutsie junk, useless for "real" CS majors. – Owen Reynolds Mar 31 '22 at 20:47
  • 1
    *Off-by-one errors are very easy to spot in these pictures* - I think that's really the key point. Not only is the desired output tied closely to each step of execution, like a debug-print and unlike for example a sorting algorithm, the actual shape will be visually wrong for some kinds of bugs. For students who might not have learned how to use a debugger yet, this code is still very debugable except in the very lowest-level languages (like assembly) where even printing numbers as ASCII digit-strings takes some work and there are calling-conventions you can get wrong. – Peter Cordes Apr 01 '22 at 20:20
  • Loops where you aren't printing something every iteration as part of the desired output don't have this feature, and debugging them might require single-stepping in a debugger (or adding debug-prints), and could still be trickier to follow. – Peter Cordes Apr 01 '22 at 20:23
  • @OwenReynolds The idea holds that it's still about learning loops and not pretty pictures. There are CS majors who start first year having never coded before. Trust me. – Rick Henderson Apr 01 '22 at 22:45
  • It's funny how one person's "cutsie junk" is another person's art. I see no harm in making CS fun, even if patterns like this didn't have obvious practical application (they do). What's the problem with making programming accessible to non-CS majors, exactly? That seems like a social imperative in this day and age to me. – ggorlen Apr 05 '22 at 15:36
2

I let my students generate Pascal's triangle as a data structure. That's an exercise in loops.

And then they have print the triangle modulo some number n: print a star when the entry modulo n is zero, a space otherwise. Instant fractals!

So there you essentially need to generate screen output.

Victor Eijkhout
  • 1,423
  • 5
  • 13
2

I think examples like these

1
1 2
1 2 3
1 2
1

1 
2 3
4 5 6
7 8 9 10
11 12 13 14 15 etc.

serve different purposes, e.g.:

  • Recognize or derive a construction (input)pattern from the optical (output) pattern
  • Numbers are probably the most primitive example of unique absolutely order-able items
  • It's probably rather easy to verify whether the solution is correct (comparing outputs)
  • the human brain is rather good detecting and recognizing patterns
  • Adding details like fancy strings instead of simple digits will just hide the essential structure

BTW: The first example is a stack where elements are popped from the right, while the second example is a stack where elements are popped at the left ;-)

U. Windl
  • 131
  • 1
  • 3
    You are right about the first one, that is a classic stack, but you missed the second one, it's a simple nested loop. You wouldn't use a stack for this and if you use a queue then you've overcomplicated things, there are better patterns to compare stacks to queues. – Chris Schaller Mar 31 '22 at 13:47
  • 3
    +1 unlike in many other exercises, the output closely matches the flow of the program, making it easy to visualize whats happening in the code. It looks just like the output of a "printf-debugged" program. – PhilipRoman Mar 31 '22 at 14:05
  • @PhilipRoman: I think [Owen Reynold's answer](https://cseducators.stackexchange.com/a/7326/1654) comes closer to saying exactly that, but yeah, I'm not sure anyone's posted an answer where that's the main point. Except the question's not asking why these are good teaching exercises, so maybe not. – Peter Cordes Apr 01 '22 at 20:27
1

Patterns are everywhere in programming, and pattern recognition is the skill that is often so noticeably absent in fresh grads. They would often prefer to copy and paste code ad infinitum rather than recognize a pattern and avoid repetition through better design, inheritance, modularity, decorators, etc. Good question.

postoronnim
  • 111
  • 2
1

Most people understand the basis of numerical sequences and can see how one set of digits changes with each iteration.

This makes numerical sequences a very good fit for teaching people iterations and iterations are usually done in a loop.

The two examples you provide show two different types of loops. One appears to be sequential numbers, increasing by one digit for each line of the output, and the other repeats the same numbers, increases and then decreases the number of digits in the output.

The display of those simple numerical digits are a lot easier to understand than my description.

Andy Gee
  • 111
  • 3
1

Are these kinds of pattern questions just for fun and to get better at using loops, or are there any interesting applications of these types of pattern questions? Or any interesting math behind them?

All of the above.

Fun is important. If learners aren't having fun, they're likely to quit. As an educator working with young children to adults from all walks of life, I've seen a lot of folks lose interest in programming, often because (I suspect) they didn't see the magic and power in it. Sure, for some learners, jumping into business applications might feel more purposeful than playing with patterns, so there is audience-specificity here.

There's something magical about loops and iterative processes in general. Being able to write a few lines of code and generate a pattern involves making the leap from 1 to 2 to 3 to an arbitrary n. Generalizing code and figuring out which parts are dynamic (i.e. parameters and variables) is a critical skill in programming. These loops provide visual feedback for this process and allow students to iterate on naive, hardcoded attempts until they've generalized the logic.

If you think of the characters on the screen as pixels, these nested loop exercises translate very well into computer graphics. I have a post on how you can animate these patterns. This is a useful teaching approach for languages other than Scratch and JavaScript that don't have a particularly easy way to make images and animations other than ASCII.

ASCII text applications like this connect students to computer history while increasing their comfort working in the always-relevant terminal ecosystem. ASCII art, Text adventures, creative coding, 10 PRINT, Figlet banners and other console-based applications are just as fun and useful today as they've ever been. npm is chock full of packages for animations, spinners, banners and colors to create user-friendly command line tools for programmers.

From a purely technical perspective, these exercises provide great opportunities for nesting conditions in loops, counting forwards and backwards, incrementing by something other than 1, understanding when for vs while is appropriate, getting comfortable with string manipulation and building up to arrays and functions. Given the fun factor and possibilities available, I don't think building an introductory course around ASCII patterns is a particulary absurd idea.

In regards to math, there can be as much or as little of it as you want in making these patterns. Consider patterns in triangular numbers, Pascal's Triangle and cellular automata, for starters. For the right audience, integrating trigonometric functions like sine and cosine functions can provide a great extension of possibilities. Fractals and L-Systems can be fun to play with, given the right resolution and reasonably experienced coders.

Finally, as an educator, puzzles like these offer a great source of assignments that are relatively difficult to find direct solutions for on the internet, potentially helping combat plagiarism. It's easy to communicate the deliverables by showing a few example runs and explaining that it should work on any n. These problems are language-agnostic.

Code Golf SE has over 1000 posts in the ascii-art tag ripe for exploration. Codewars has a good deal of ASCII shape printing problems with test cases (disclosure: I consult for the company that owns Codewars).

ggorlen
  • 573
  • 2
  • 8