Fundamentals of Programming - Lesson 3: Structured Programming Notes

Spaghetti Code and the Case for Structure

  • Professional programs can be extremely complex; even routine tasks (airplane guidance, tax returns, payroll) involve many instructions. Large, unstructured code tends to become a tangled, hard-to-maintain mess called spaghetti code.
  • Spaghetti code is the descriptive name for unstructured program statements that do not follow the rules of structured logic. It is prone to errors, hard to reuse, and difficult to extend.
  • Structured programs follow the rules of structure (sequence, selection, loop), which eliminates many problems associated with spaghetti code.

The Disadvantages of Unstructured Spaghetti Code

  • Difficult to read and maintain as logic grows.
  • Hard to reuse components for larger applications.
  • Higher likelihood of errors due to tangled control flow.
  • The example dog-washing flowchart (Figure 3-1) illustrates how unstructured logic can still produce correct results but be unreadable and brittle.

The Three Basic Structures

  • A structure is a basic unit of programming logic; there are three: sequence, selection, and loop.
  • With these three structures, any task can be diagrammed or coded, from simple number doubling to complex procedures.
  • Mathematically grounded idea: any program, no matter how complex, can be constructed using one or more of only these three structures.

The Sequence Structure

  • Performs actions in a fixed order, one after another.
  • No branching or choices; you must complete each step before moving to the next.
  • Example analogy: giving driving directions in a fixed order.
  • Pseudocode/flow example: a1; a2; a3 (execute in this order; no branching).
  • Diagram reference: Figure 3-2 shows a sequence structure.

extSequence:exta<em>1ightarrowexta</em>2<br/>ightarrowexta3<br/>ightarrowextext{Sequence: } ext{a}<em>1 ightarrow ext{a}</em>2 <br /> ightarrow ext{a}_3 <br /> ightarrow ext{…}

The Selection Structure (Decision Structure)

  • Chooses between two courses of action based on a condition (Boolean expression).
  • A decision begins with a Boolean test; the branches must join at the bottom of the structure.
  • Boolean expressions yield true/false (or yes/no, 1/0).
  • Pseudocode convention: begins with if; ends with endif. Often described as if-then-else.
  • Dual-alternative selections have two branches: if true do A else do B; endif.
  • Single-alternative selections have an action only when the condition is true; the false branch is a null action (null case).
  • Example: if traffic is backed up on Washington Boulevard then continue for 1 block on First Avenue and turn left on Adams Lane else turn left on Washington Boulevard endif
  • Payroll example: if hoursWorked > 40 then regularPay and overtimePay else regularPay endif
  • True/false flow: tests start with a decision symbol in flowcharts; end structures end with endif; flow joins back after decision.
  • Key concept: II The if-else can be represented as a dual-alternative selection; the null branch is valid for single-alternative cases.
  • Notation:
    • Condition: ext{hoursWorked} > 40
    • Action: then compute regularPay and overtimePay; else compute regularPay

The Loop Structure

  • A loop repeats actions while a tested condition remains true (repetition/iteration).
  • In the most common type (while loop), a condition is evaluated before each iteration; if true, the loop body executes, then the condition is evaluated again.
  • Flowchart starts with a decision symbol; the loop returns to the evaluation after the loop body.
  • The loop can also be a do-while type (not the focus here); this chapter assumes a while loop that tests before the first execution.
  • Loop body continues until the condition becomes false, then you exit the loop.
  • Everyday examples of looping include hungry -> eat another bite -> check hunger again; unread pages -> read next page -> check if more pages remain.
  • Notation: while testCondition continues to be true do someProcess endwhile
  • The loop’s controlling condition must be tested again after each iteration; otherwise, it would not be a loop.
  • Quick note: some languages call this a while…do loop; the generic form is shown in Figures 3-5, 3-11, etc.

extWhileexttestConditionextistrue:extsomeProcessextendwhileext{While } ext{testCondition} ext{ is true: } ext{someProcess} ext{ endwhile}

Understanding the Three Basic Structures Together

  • The three structures can be combined in unlimited ways; stacking means placing them end-to-end, such as sequence followed by a selection followed by a loop (Figure 3-6).
  • Nesting means placing one structure inside another (e.g., a loop containing a sequence, or a selection inside a loop) (Figures 3-7 to 3-9).
  • Placement: every structure has a single entry point and a single exit point; structures can attach to one another only at entry/exit points; they cannot overlap.
  • There is no limit to nesting depth.

Understanding Structure: Combined and Nested Examples

  • Example: sequence followed by selection, which contains a block that may include a loop (Figure 3-6).
  • Example: a sequence on one branch of a selection, with another structure inside (Figure 3-7).
  • Example: a loop (while) that contains a nested selection (Figure 3-9) and other sequences inside the loop body (Figures 3-8, 3-9).
  • The flowchart and the pseudocode represent the same logic; indentation in pseudocode shows nesting relationships.
  • Nested structures are aligned and indented to reflect their levels; you should be able to follow the flow by tracing entry/exit points.

Priming Input and End-of-Data Concepts

  • Priming input (or priming read) is an initial input statement that starts the loop, ensuring there is a first value to process.
  • The last input statement within the loop obtains the next data value so subsequent iterations have a value to process.
  • End-of-data condition (eof) is a sentinel that signals termination; the loop ends when eof is encountered.
  • The recommended structure is: priming input, test not eof? before each iteration, process, fetch next input at loop end; terminate when not eof?
  • Figure 3-16 shows a structured solution with a priming input; Figure 3-11 shows an unstructured version; Figure 3-14 shows a flawed structured version without proper reset to the next input.

extnoteof?extistruewhilemoredataremainstoberead.ext{not eof?} ext{ is true while more data remains to be read.}
exteofextstandsforendoffile(data)sentinel.ext{eof} ext{ stands for end-of-file (data) sentinel.}

  • Important rule: A program-ending test should occur immediately after an input statement so the loop can exit as soon as data stops.

Spaghetti Bowl Method and Restructuring Unstructured Logic

  • If a segment is unstructured, you can untangle it like a bowl of spaghetti to identify natural sequence, selection, or loop regions (the "spaghetti bowl" method).
  • Steps to untangle: identify sequences, determine conditional tests that begin selections, and trace paths until you can clearly group them into structured blocks.
  • The goal is to convert unstructured flowcharts into structured ones by grouping into sequences, selections, and loops that have proper entry/exit points and can be nested or stacked.

Dog-Washing Example: From Spaghetti to Structured

  • Original Figure 3-1 shows an unstructured spaghetti code flowchart for washing a dog; it may work but is hard to read and maintain.
  • A structured version (Figure 3-21 and 3-22) uses a combination of sequence and loop; the logic is clearer and easier to maintain.
  • Key steps in a structured dog-washing flow: Catch dog (sequence), test if dog runs away (decision); if runs away, loop to catch again; if not, turn on water, apply shampoo, rinse, etc., returning to the decision as appropriate.
  • A practical enhancement: modularize repeated blocks (Figure 3-23) by placing recurring logic into modules like catchDogStartWater(); this reduces duplication and improves maintainability.
  • Benefits of modularization: shorter main program, easier updates, and centralized changes (e.g., adding temperature checks when turning on water would be added in one module instead of three places).
  • The final structured version shows alternating sequence and loop with nested blocks and can be further modularized for even clearer structure.

Why Structure Matters: Reasons for Using Only Three Structures

  • Clarity: Structured programs are easier to read and understand, especially as programs grow larger.
  • Professionalism: Structured approaches are expected by programmers and educators; modern languages enforce or encourage structured styles.
  • Efficiency: Most modern languages have concise syntax for sequence, selection, and looping; older languages can still be written in a structured form.
  • Maintenance: Structured programs are easier to modify and extend; future changes are less error-prone.
  • Modularity: Structured programs facilitate modular coding where routines can be reused across programs.
  • The three basic structures, when properly nested and stacked, can express any logic; there is no limit to nesting depth.

Quick Reference: Structures in Flowcharts and Pseudocode

  • Each structure has a single entry point and a single exit point.
  • Stacking: sequences, selections, and loops can be attached end-to-end to form longer logic.
  • Nesting: one structure can be placed inside another; indentation in pseudocode reflects nesting.
  • Relationships:
    • Sequence → selection or loop can begin once the previous step completes.
    • Selection may contain sequences or loops inside its branches.
    • Loop may contain sequences or nested structures inside its body.
  • The flowchart and pseudocode are equivalent representations of the same logic.
  • Generic examples use placeholders (e.g., stepA, conditionC) to focus on structure rather than specific tasks.

Practical Implications and Real-World Relevance

  • In real-world software, structured programming supports maintainability, readability, and collaboration across teams.
  • Modern languages (e.g., C#, Java, C++) enforce structured programming through syntax and idioms that make stacking and nesting natural.
  • The priming input and end-of-data concepts are widely used in data-driven programs and streaming data processing.
  • The idea of modularizing repeated blocks aligns with software engineering practices like functions, methods, and modules.

Summary of Key Takeaways

  • Spaghetti code is unstructured and undesirable due to readability, maintainability, and reliability concerns.
  • Only three basic structures are needed to express all logic: sequence, selection, and loop.
  • Each structure has a single entry and exit point; structures can be stacked and nested to form complex logic.
  • A priming input is useful to start a structured loop; the last step inside the loop should typically update the loop’s controlling condition (e.g., not eof?).
  • End-of-data (eof) testing should occur immediately after an input operation to determine whether the loop should continue.
  • Unstructured flow segments can be untangled using the spaghetti-bowl method to reveal underlying structure.
  • Modularity and maintenance benefits come from modularizing repeated logic blocks (e.g., dog-washing steps) and using structured approaches.

Two Truths & A Lie (Review)

  • 1. Structured programs are clearer than unstructured programs. (True)
  • 2. You and other programmers will find it easier to modify and maintain structured programs as changes are required in the future. (True)
  • 3. Structured programs are not easily divided into parts, making them less prone to error. (Lie — structured programs are easily modularized and divided into parts)

References to Figures and Concepts (Contextual Notes)

  • Figure 3-1: Spaghetti code flow for washing a dog (unstructured example).
  • Figure 3-2: Sequence structure.
  • Figure 3-3: Selection structure (decision).
  • Figure 3-4: Single-alternative selection (no else).
  • Figure 3-5: Loop structure (while loop).
  • Figure 3-6: Stacking structures example (sequence → selection → loop).
  • Figure 3-7 to 3-9: Nested structures; shows how to place one structure inside another.
  • Figure 3-11 to 3-16: Discussion of structured vs unstructured loop, end-of-data handling, priming input.
  • Figure 3-21 to 3-23: Dog-washing program structured and then modularized.
  • Quick Reference 3-1: Three basic structures and their entry/exit points.
  • Figures 3-18 to 3-20: Examples of structure recognition and the spaghetti-bowl method for untangling.

Additional Notes on Terminology and Concepts

  • Structure: a basic unit of programming logic (sequence, selection, loop).
  • Boolean expression: expression whose value can be true or false; controls selection and loop decisions.
  • End-of-data (eof): sentinel indicating the end of input data; used to terminate loops.
  • Priming input: initial input that starts a structured loop; the final input inside the loop provides the data for subsequent iterations.
  • Null branch: in a single-alternative selection, the branch that performs no action when the condition is false.
  • Indentation in pseudocode reflects nesting levels and logical blocks; helps readability and maintenance.
  • Structured programming is foundational for modern software practices, enabling clearer collaboration and easier maintenance across languages.