Zum Inhalt springen

English:Functions, Procedures, and Modularity

Aus MOOCsWiki Staging
Version vom 27. August 2026, 14:17 Uhr von Glanz (Diskussion | Beiträge) (aiMOOC über GPT aiMOOC Action erstellt)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
aiMOOC-Siegel

Functions, Procedures, and Modularity



Introduction

Functions, Procedures, and Modularity are core ideas in computer programming. They help you turn a large problem into smaller, understandable parts. Instead of writing one long program from top to bottom, you can create named blocks of code that each do one clear job. You can then call those blocks whenever they are needed.

This aiMOOC is designed for Grades 9–10. You will learn how functions and procedures work, how information moves into and out of them, how variable scope affects a program, and how modular design improves readability, testing, debugging, teamwork, and reuse. Examples use Python-style code because it is easy to read, but the main ideas apply to many programming languages.

A flowchart is useful before coding because it shows how a larger process can be divided into smaller steps. In programming, the same idea becomes decomposition: breaking a complex task into smaller subproblems that can be designed, implemented, and tested separately.


Why Programs Need Structure

A short program can fit on one screen. A larger program may contain hundreds, thousands, or millions of lines of code. If every instruction is mixed together in one place, it becomes difficult to answer simple questions: Where is a calculation performed? Which code handles input? Which part should be tested? What will break if one section changes?

Modularity is a way to organize a system as separate, meaningful parts called modules. A module may be a file, class, package, library, or another unit of organization, depending on the language and project. Inside modules, programmers commonly use functions or procedures to represent individual tasks.

Good modular design has several benefits:

  1. Readability: Smaller named parts make the program easier to understand.
  2. Reusability: A useful function can be called many times instead of copied.
  3. Testability: Small units can be checked independently.
  4. Debugging: Errors are easier to locate when responsibilities are separated.
  5. Collaboration: Team members can work on different modules with clearer boundaries.
  6. Maintainability: A change in one part is less likely to require rewriting the whole program.

A useful guiding question is: Can I describe the purpose of this part in one clear sentence? If not, the part may be doing too many jobs.


Decomposition and Abstraction

Decomposition means breaking a large problem into smaller problems. For example, a school quiz program could be decomposed into tasks such as displaying a question, collecting an answer, checking the answer, updating the score, and showing the final result.

Abstraction means using a component through a clear interface without needing to think about every internal detail each time. When you call a function named calculate_average, you can focus on what the function promises to do. You do not need to reread its entire implementation every time you use it.

These ideas work together. Decomposition creates manageable parts; abstraction lets you use those parts at a higher level.


Functions and Procedures

A subroutine is a named block of instructions that can be called from another part of a program. Different programming languages and school curricula use terms such as function, procedure, method, routine, or subprogram.

In many introductory courses, the terms are distinguished like this:

  1. Function: A subroutine designed to produce and return a value.
  2. Procedure: A subroutine designed mainly to perform an action, such as displaying text or changing stored data.

The exact terminology depends on the programming language. For example, Python uses the word function for both value-producing and action-performing subroutines. A Python function with no explicit return statement returns the special value None. It can still behave like what some courses call a procedure.


Defining and Calling a Function

A function normally has a definition and one or more calls. The definition tells the computer what the function should do. A call asks the computer to execute that function.

def greet():
    print("Hello, learner!")

greet()
greet()

The function greet is defined once and called twice. This avoids copying the same print statement wherever the greeting is needed.

A good function name is short but meaningful. Names such as calculate_total, is_valid_password, and display_menu communicate purpose better than names such as thing or do_it.


Parameters and Arguments

A function becomes more reusable when it can work with different input values. A parameter is a named input listed in the function definition. An argument is the actual value supplied when the function is called.

def greet(name):
    print("Hello, " + name + "!")

greet("Maya")
greet("Jordan")

Here, name is a parameter. "Maya" and "Jordan" are arguments in two different calls.

With more than one parameter, the order usually matters:

def rectangle_area(width, height):
    return width * height

area = rectangle_area(8, 5)

The call passes 8 to width and 5 to height.


Return Values

A return value is information sent back from a function to the code that called it. Returning a value is different from printing it.

def double(number):
    return number * 2

result = double(7)
print(result)

The function returns 14. The calling code stores that value in result and then prints it.

Compare that with this procedure-like function:

def show_double(number):
    print(number * 2)

The second version displays a result but does not provide the doubled number as a useful return value for later calculations. A returned value is often more flexible because the caller can decide what to do with it.

Datei:Flowgorithm Functions Main.svg

A main program can call smaller functions to handle parts of a larger task. This keeps the high-level flow readable while the details remain inside well-named components.


Designing Useful Functions

A well-designed function usually has one focused responsibility. A function named calculate_tax should calculate tax. It should not also ask for a password, save a file, play a sound, and change unrelated settings.

One common design approach is:

  1. Identify a small task with a clear purpose.
  2. Decide what information the task needs.
  3. Decide what result, if any, it should return.
  4. Give the function a descriptive name.
  5. Write the function.
  6. Test it with normal, boundary, and unusual inputs.
  7. Use it from the larger program.

This process turns a vague coding problem into a set of smaller decisions.


Inputs, Processing, and Outputs

You can think of many functions as an input–process–output unit.

For a function that calculates a percentage score:

  1. Input: points earned and points possible.
  2. Processing: divide the earned points by the possible points and multiply by 100.
  3. Output: the percentage.
def percentage_score(points_earned, points_possible):
    return points_earned / points_possible * 100

Before using this function in a real program, you should also consider invalid input. For example, points_possible must not be zero. Good modular design does not remove the need for validation; it gives validation a clear place to happen.


Preconditions and Postconditions

A precondition describes what must be true before a function runs correctly. A postcondition describes what the function guarantees after it finishes, assuming the preconditions were satisfied.

For percentage_score, a simple precondition could be: points_possible is greater than zero. A postcondition could be: the function returns the calculated percentage as a number.

Thinking about preconditions and postconditions helps you define a clear contract between a function and the code that calls it.


Variable Scope

Scope describes where a variable name can be accessed. Variables created inside a function are usually local variables. They belong to that function call and are not normally available everywhere else.

def calculate_total(price, quantity):
    total = price * quantity
    return total

cost = calculate_total(4.50, 3)
print(cost)

The variable total is local to calculate_total. The variable cost is created outside the function.

Local variables are useful because they reduce accidental interference between different parts of a program. When a function controls its own temporary data, other code is less likely to change that data unexpectedly.

Global variables are defined in a wider scope and may be accessible from many parts of a program. They can be useful in some situations, but overusing them can make a program harder to understand because many functions may depend on or modify the same shared state.

A good beginner rule is: prefer explicit parameters and return values when practical, rather than using global variables to move information between functions.


What Happens During a Function Call?

When a function is called, the program temporarily transfers control to that function. The function receives its arguments, creates the local information it needs, performs its instructions, and then returns control to the caller.

Many programming systems use a call stack to keep track of active function calls. Each active call has a stack frame containing information needed for that call, such as local variables and where execution should continue afterward.

Datei:Call stack layout.svg

You do not need to manage the call stack directly in ordinary beginner programs. However, understanding that separate function calls have separate local contexts helps explain scope, nested calls, and some error messages.


Nested Function Calls

One function can call another:

def square(number):
    return number * number

def sum_of_squares(a, b):
    return square(a) + square(b)

answer = sum_of_squares(3, 4)

To understand this code, trace one call at a time. sum_of_squares calls square for 3, then for 4, and adds the returned values. The final result is 25.

Tracing nested calls is easier when each function has one clear purpose.


Modularity Across Files

Functions help organize code inside a file. Modules help organize related code across larger parts of a program. In Python, a module is commonly a .py file containing definitions and statements that another file can import.

Imagine a game with these responsibilities:

  1. player.py: Player movement and health.
  2. inventory.py: Items, adding, removing, and counting.
  3. combat.py: Damage calculations and battle rules.
  4. main.py: High-level game flow.

The goal is not to create as many files as possible. The goal is to group related responsibilities so that each module has a clear reason to exist.


Cohesion and Coupling

Two useful ideas for judging modular design are cohesion and coupling.

High cohesion means the parts inside a module are strongly related to the module's purpose. An inventory module that contains item storage, item lookup, and item removal has a coherent theme.

Low coupling means modules depend on each other as little as practical. If changing one module forces many unrelated modules to change, the design may be too tightly connected.

A useful target is high cohesion and low coupling. For Grades 9–10, you can apply this as a question: Do the things inside this part belong together, and can this part be changed without surprising effects elsewhere?


Reuse and the DRY Principle

Repeated code is a warning sign. If the same logic appears in several places, a function may allow you to define it once and reuse it.

Suppose a program calculates the area of several rectangles. Copying width * height everywhere may seem harmless, but repeated logic becomes risky when the rule later changes or requires validation.

def rectangle_area(width, height):
    return width * height

The idea is often summarized as DRY: Don't Repeat Yourself. DRY does not mean that every repeated line must become a function. It means that important knowledge or logic should usually have one clear source instead of several copies that can become inconsistent.


Side Effects and Predictable Behavior

A side effect is a change a function makes outside its returned value, such as printing text, writing a file, changing a global variable, or modifying an object.

Side effects are not automatically bad. A procedure that saves a game must change something outside itself. The design question is whether the side effect is necessary, clear, and easy to reason about.

Compare these two styles:

def calculate_discount(price, rate):
    return price * rate

def announce_discount(price, rate):
    discount = price * rate
    print("Discount:", discount)

The first function computes and returns a value. The second also performs output. Keeping calculation separate from display often makes testing easier because you can check returned values directly.


Testing Functions and Procedures

Modularity makes testing more precise. Instead of asking only, "Does the whole program work?", you can ask, "Does this function work for these inputs?"

For a function is_even(number), useful tests include:

  1. A positive even number.
  2. A positive odd number.
  3. Zero.
  4. A negative even number.
  5. A negative odd number.
def is_even(number):
    return number % 2 == 0

assert is_even(8) == True
assert is_even(7) == False
assert is_even(0) == True

An assertion checks that a condition is true. When a test fails, you know which small component needs investigation.

Datei:Flowgorithm Functions DisplayResult.svg

Separating calculation from display can make both parts easier to test. One function can prepare a result, while another can handle presentation.


Boundary Cases and Error Handling

A function should be tested not only with typical values but also with boundary cases. Boundary cases are inputs near important limits, such as zero, an empty string, the smallest allowed value, or the largest allowed value.

You should also decide how invalid input is handled. A function might reject it, return a special result, or raise an error, depending on the language and the design. The important point is that the behavior should be deliberate and understandable.


Refactoring Toward Modularity

Refactoring means improving the internal structure of code without intentionally changing what the program does for the user.

Consider this repetitive code:

score1 = 16 / 20 * 100
score2 = 42 / 50 * 100
score3 = 27 / 30 * 100

A refactored version can express the idea once:

def percent(earned, possible):
    return earned / possible * 100

score1 = percent(16, 20)
score2 = percent(42, 50)
score3 = percent(27, 30)

The refactored version communicates the purpose through the name percent. It also gives you one place to add validation or change the calculation later.


Common Mistakes

Mistake 1: Calling a function without using its return value. If a function returns a result, the caller usually needs to store, print, compare, or otherwise use it.

Mistake 2: Confusing parameters and arguments. Parameters appear in the definition; arguments appear in a call.

Mistake 3: Printing when a return value is needed. Printing shows information to a user, while returning sends information back to code.

Mistake 4: Depending heavily on global variables. Hidden dependencies make functions harder to test and reuse.

Mistake 5: Creating functions that do too much. A large function with many unrelated jobs may need further decomposition.

Mistake 6: Choosing vague names. Names should communicate purpose.

Mistake 7: Splitting code into tiny pieces without a reason. Modularity should improve understanding, not create unnecessary complexity.


A Worked Example: Modular Quiz Program

Suppose you want to create a simple quiz. Start by identifying responsibilities:

  1. Present a question.
  2. Collect an answer.
  3. Check whether the answer is correct.
  4. Update the score.
  5. Repeat for more questions.
  6. Display the final score.

One possible design is:

def ask_question(prompt):
    return input(prompt + " ")

def is_correct(user_answer, correct_answer):
    return user_answer.strip().lower() == correct_answer.lower()

def run_question(prompt, correct_answer):
    answer = ask_question(prompt)
    if is_correct(answer, correct_answer):
        print("Correct!")
        return 1
    print("Not quite.")
    return 0

def main():
    score = 0
    score += run_question("What keyword defines a function in Python?", "def")
    score += run_question("What word sends a value back to the caller?", "return")
    print("Final score:", score)

main()

Notice the responsibilities. ask_question handles input, is_correct checks text, run_question manages one question, and main shows the high-level flow.

The design could be improved further by storing questions in a list and separating user interface code from quiz logic. The important point is that modular design gives you understandable places for future changes.


Choosing Between a Function, Procedure, and Module

Use a function when you want a reusable operation that computes or returns a result. Use a procedure-like routine when the main purpose is to perform an action. Use a module when several related functions, constants, or classes belong together as a larger unit.

A single project can use all three ideas. For example, a weather application may have a weather_math module, a function that converts Celsius to Fahrenheit, and a procedure-like function that displays a formatted report.

Remember that language terminology varies. Design responsibilities matter more than labels.


Professional Connections

Functions and modularity appear in many careers and technical fields. A software developer uses them to build maintainable applications. A web developer separates authentication, data access, and page logic. A data scientist creates reusable analysis functions. A game developer organizes movement, scoring, inventory, and physics systems. A cybersecurity specialist uses modular scripts to automate checks. A robotics programmer separates sensing, decision-making, and motor control.

The same thinking also appears outside coding. Engineers divide systems into components, scientists divide experiments into stages, and project teams divide complex goals into manageable responsibilities. Modularity is both a programming technique and a general problem-solving strategy.


Interactive Tasks


Quiz: Test Your Knowledge

What is the main purpose of modularity in programming? (To divide a complex program into manageable parts) (!To make every program use only one file) (!To remove the need for testing) (!To prevent functions from being reused)




What is a parameter? (A named input in a function definition) (!The value returned by a function) (!A comment written after a function) (!A file that stores a whole program)




What is an argument? (A value supplied when a function is called) (!A variable that can only be global) (!A type of programming error) (!A rule that prevents function calls)




What does a return statement do? (Sends a result back to the caller) (!Always prints a result on the screen) (!Creates a new module file) (!Makes every variable global)




Which description best matches a local variable? (A variable used within a limited scope such as a function) (!A variable that every module must share) (!A variable that can never change) (!A variable stored only on the internet)




Why can small functions improve testing? (They let you check one focused behavior at a time) (!They guarantee that bugs cannot exist) (!They make test inputs unnecessary) (!They remove all dependencies automatically)




What does high cohesion mean? (Elements in a module are strongly related to one purpose) (!Every module depends on every other module) (!A function must contain many unrelated tasks) (!All variables must use the same name)




What does low coupling mean? (Modules have limited unnecessary dependencies on each other) (!Modules must never communicate) (!Every function must be copied into each file) (!All program logic belongs in one module)




What is refactoring? (Improving code structure without intentionally changing behavior) (!Adding random features to a finished program) (!Deleting all functions from a program) (!Replacing tests with comments)




Which practice usually makes information flow clearer? (Using parameters and return values deliberately) (!Using many unrelated global variables) (!Giving every function the same name) (!Copying the same logic into many places)





Memory Game

Parameter Named input in a function definition
Argument Actual value supplied in a function call
Return value Result sent back to the caller
Scope Region where a variable name can be accessed
Module Unit that groups related program components
Refactoring Improving internal code structure while preserving intended behavior





Drag and Drop

Match the correct terms. Topic
Parameter Named input in a function definition
Argument Value supplied in a function call
Local variable Name used within a limited scope
Return value Result passed back to the caller
Module Group of related program components




Match each programming term with the description that belongs to it. Then explain one match in your own words to a partner.


Crossword Puzzle

Function What named program unit can be called to perform a task?
Parameter What is a named input in a function definition?
Argument What value is supplied when a function is called?
Modularity What design idea divides a system into manageable parts?
Cohesion What quality means related elements belong together?
Refactoring What process improves code structure without intentionally changing behavior?





LearningApps


Cloze Text

Complete the text.

Programmers use

to divide a complex system into manageable parts. A named reusable block of instructions can be called a

. A named input in a function definition is a

. The actual value supplied during a call is an

. A function can send a result back to its caller with a

statement. A variable created inside a function usually has

scope. Strongly related responsibilities inside one module are described as high

. Reducing unnecessary dependencies between modules produces lower

. Improving internal code structure while keeping intended behavior the same is called

.




Open-Ended Tasks


Easy

  1. Function vocabulary poster: Create a one-page poster that explains function, procedure, parameter, argument, return value, and module in your own words, with one small code example.
  2. Trace a function call: Draw arrows showing how two arguments enter a function and how one return value comes back to the caller, then explain the flow to a partner.
  3. Reusable calculator: Write three small functions for a calculator, test each one with at least three inputs, and record the results.
  4. Programming interview: Interview a classmate about where repeated code appears in a small program and suggest one function that could replace the repetition.


Standard

  1. Refactor a repeated program: Take a short program with repeated calculations, refactor the repeated logic into functions, and compare the before-and-after versions for readability.
  2. Modular quiz project: Build a small quiz with separate functions for asking, checking, scoring, and displaying results, then test each function independently where practical.
  3. Scope experiment: Write a program that uses local and global variables, predict what each line will do, run the program, and explain any difference between your prediction and the result.
  4. Code review video: Record a two- to four-minute screen video explaining how parameters, return values, and modular design work in one of your own programs.


Advanced

  1. Module design project: Design a small game or utility split across at least three meaningful modules, draw the dependency relationships, and justify how you tried to keep cohesion high and coupling low.
  2. Function testing investigation: Create a reusable function with input validation, design normal and boundary test cases, run them, and present a test report that explains failures and fixes.
  3. Interface redesign challenge: Choose a function with an unclear interface, redesign its name, parameters, return value, and error behavior, then defend your decisions using the idea of a function contract.
  4. Local software practice study: Interview a programmer, coding teacher, or software team member about how they organize large programs, then compare their explanation with the modularity principles from this aiMOOC.



Learning Assessment

  1. Decomposition assessment: Given a description of a school event app, divide it into modules and functions, explain the responsibility of each part, and justify why your design is easier to maintain than one large program.
  2. Function interface assessment: Design a function for calculating a delivery cost, specify its parameters, preconditions, return value, and test cases, and explain how the interface supports reuse.
  3. Scope reasoning assessment: Analyze a program that mixes local and global variables, predict its behavior, identify one risk caused by shared state, and rewrite part of it using parameters and return values.
  4. Refactoring assessment: Refactor a repetitive code sample into smaller functions and explain which duplication was removed, how naming improved abstraction, and whether any new dependencies were introduced.
  5. Testing transfer assessment: Design tests for an unfamiliar function, including normal, boundary, and invalid cases, and explain how a failure would help you locate a defect.
  6. Modularity trade-off assessment: Compare two designs for the same program, one highly centralized and one divided into modules, and argue which design is more suitable while acknowledging at least one trade-off.




Evidence of Learning

Strong evidence of learning includes both what you know and what you can produce. You should be able to explain the difference between definitions and calls, parameters and arguments, printing and returning, and local and global scope. You should be able to trace simple nested function calls and describe why the call stack keeps active calls separate.

Your practical evidence should include working functions with clear names, meaningful parameters, appropriate return values, and focused responsibilities. A larger program should show purposeful decomposition into modules rather than arbitrary splitting. Test evidence should include expected results, boundary cases, and corrections made after failures.

Your products may include annotated code, flowcharts, module diagrams, test tables, refactored before-and-after examples, short presentations, screen recordings, or project documentation. The strongest transfer evidence is your ability to apply modular thinking to a new problem, explain design trade-offs, and justify why your structure improves readability, reuse, testability, or maintainability.




OERs on the Topic

The English Wikipedia article on Subroutine provides background on functions, procedures, routines, and related terminology. You can also explore Modular programming, Scope (computer science), Call stack, and Software testing for connected concepts.



Linked Learning Areas


aiMOOC Projects

MOOCwiki · Deutsch

Nach dem Lernen ist vor dem Lernen

Entdecke direkt den nächsten Lernkurs. Weitere Inhalte erscheinen, wenn Du weiter nach unten scrollst.

Zur MOOCwiki-Hauptseite

Mediathek

Mediathek

Inhalte werden geladen ...

Mediathek wird aus dem Wiki geladen ...