This is the multi-page printable view of this section. Click here to print.
Starting Concepts
1 - Variables and Data Types
Variables
A variable is a container to store data in the computer’s memory. We can think of it as a box with a label. The label is the variable name and inside the box its value is stored.
To declare a variable in Python we just write the name and assign a value:
age = 28
price = 19.95
student = True
Variable names must start with letters or underscore, and can only contain letters, numbers and underscores. It is recommended to use meaningful names that represent the purpose of the variable.
In Python variables do not need to be declared with a particular type. The type is inferred automatically when assigning the value:
age = 28 # age is integer type
price = 19.95 # price is float type
single = True # single is boolean type
Once assigned, a variable can change its value at any time:
age = 30 # We change age to 30
Scope and lifetime
The scope of a variable refers to the parts of the code where it is available. Variables declared outside functions are global and available throughout the file. Variables inside a function are local and only visible within it.
The lifetime is the period during which the variable exists in memory. Local variables exist while the function executes, then they are destroyed. Global variables exist while the program is running.
Assignment
Assignment with the =
operator allows changing or initializing a variable’s value:
number = 10
number = 20 # Now number is 20
There are also compound assignment operators like +=
and -=
that combine an operation with assignment:
number += 5 # Adds 5 to number (number = number + 5)
number -= 2 # Subtracts 2 from number
Data types
Data types define what kind of value a variable can store. Python has several built-in types, including:
Numerical: To store integer, float, and complex numeric values:
integer = 10
float = 10.5
complex = 3 + 4j
Strings: To store text:
text = "Hello World"
Boolean: For True or False logical values:
true_variable = True
false_variable = False
Collections: To store multiple values like lists, tuples and dictionaries:
Lists: Mutable sequences of values:
list = [1, 2, 3]
Tuples: Immutable sequences of values:
tuple = (1, 2, 3)
Dictionaries: Key-value pair structures:
dictionary = {"name":"John", "age": 20}
It is important to choose the data type that best represents the information we want to store.
Operators
Operators allow us to perform operations with values and variables in Python. Some common operators are:
Arithmetic:
+, -, *, /, %, //, **
Comparison:
==, !=, >, <, >=, <=
Logical:
and, or, not
Assignment:
=, +=, -=, *=, /=
Let’s see concrete examples of expressions using these operators in Python:
# Arithmetic
5 + 4 # Addition, result 9
10 - 3 # Subtraction, result 7
4 * 5 # Multiplication, result 20
# Comparison
5 > 4 # Greater than, result True
7 < 10 # Less than, result True
# Logical
True and False # Result False
True or False # Result True
not True # Result False
# Assignment
number = 10
number += 5 # Adds 5 to number, equivalent to number = number + 5
Each type of operator works with specific data types. We must use them consistently according to our variable data types.
Type conversions
Sometimes we need to convert one data type to another to perform certain operations. In Python we can convert explicitly or implicitly:
Explicit: Using functions like int()
, float()
, str()
:
float = 13.5
integer = int(float) # converts 13.5 to 13
text = "100"
number = int(text) # converts "100" to 100
Implicit: Python automatically converts in some cases:
integer = 100
float = 3.5
result = integer + float # result is 103.5, integer converted to float
Some conversions can generate data loss or errors:
float = 13.5
integer = int(float)
print(integer) # 13, decimals are lost
To prevent this we must explicitly choose conversions that make sense for our data.
Cheers for making it this far! I hope this journey through the programming universe has been as fascinating for you as it was for me to write down.
We’re keen to hear your thoughts, so don’t be shy – drop your comments, suggestions, and those bright ideas you’re bound to have.
Also, to delve deeper than these lines, take a stroll through the practical examples we’ve cooked up for you. You’ll find all the code and projects in our GitHub repository learn-software-engineering/examples-programming.
Thanks for being part of this learning community. Keep coding and exploring new territories in this captivating world of software!
Conclusion
In this article we reviewed key concepts like variables, operators, data types and conversions in Python. Applying these concepts well will allow you to efficiently manipulate data in your programs. I recommend practising with your own examples to gain experience using these features. Good luck in your Python learning!
2 - Input and output operations
Screen output
Python also provides functions to send program output to “standard output”, usually the screen or terminal1.
The print()
function displays the value passed as a parameter:
name = "Eric"
print(name) # displays "Eric"
We can print multiple values separated by commas2:
print("Hello", name, "!") # displays "Hello Eric!"
We can also use literal values without assigning to variables3:
print("2 + 3 =", 2 + 3) # displays "2 + 3 = 5"
Output formatting
Python provides various ways to format output4:
f-Strings: Allow inserting variables into a string:
name = "Eric"
print(f"Hello {name}") # displays "Hello Eric"
%s: Inserts string text into a format string:
name = "Eric"
print("Hello %s" % name) # displays "Hello Eric"
%d: Inserts integer numbers:
value = 15
print("The value is %d" % value) # displays "The value is 15"
.format(): Inserts values into a format string:
name = "Eric"
print("Hello {}. Welcome".format(name))
# displays "Hello Eric. Welcome"
These formatting options allow us to interpolate variables and values into text strings to generate custom outputs. We can combine multiple values and formats in a single output string2.
Keyboard input
Python provides built-in functions to read data entered by the user at runtime. This is known as “standard input”4.
The input()
function allows reading a value entered by the user and assigning it to a variable. For example:
name = input("Enter your name: ")
This displays the message “Enter your name: " and waits for the user to enter text and press Enter. That value is assigned to the name
variable2.
The input()
function always returns a string. If we want to ask for a number or other data type, we must convert it using int()
, float()
, etc1:
age = int(input("Enter your age: "))
pi = float(input("Enter the value of pi: "))
Reading multiple values
We can ask for and read multiple values on the same line separating them with commas3:
name, age = input("Enter name and age: ").split()
The split()
method divides the input into parts and returns a list of strings. We then assign the list elements to separate variables.
We can also read multiple lines of input with a loop4:
names = [] # empty list
for x in range(3):
name = input("Enter a name: ")
names.append(name)
This code reads 3 names entered by the user and adds them to a list.
Output to a file
In addition to printing to the screen, we can write output to a file using the open()
function1:
file = open("data.txt", "w")
This opens data.txt
for writing (“w”) and returns a file object.
Then we use file.write()
to write to that file3:
file.write("Hello World!")
file.write("This text goes to the file")
We must close the file with file.close()
when finished4:
file.close()
We can also use with
to open and automatically close:
with open("data.txt", "w") as file:
file.write("Hello World!")
# no need to close, it's automatic
Reading files
To read a file we use open()
in “r” mode and iterate over the file object1:
with open("data.txt", "r") as file:
for line in file:
print(line) # prints each line of the file
This prints each line, including newlines.
We can read all lines to a list with readlines()
3:
lines = file.readlines()
print(lines)
To read the full content to a string we use read()
4:
text = file.read()
print(text)
We can also read a specific number of bytes or characters with read(n)
2.
File handling operations
There are several built-in functions to handle files in Python1:
open()
- Opens a file and returns a file objectclose()
- Closes the filewrite()
- Writes data to the fileread()
- Reads data from the filereadline()
- Reads a line from the filetruncate()
- Empties the fileseek()
- Changes the reading/writing positionrename()
- Renames the fileremove()
- Deletes the file
These allow us to perform advanced operations to read, write and maintain files.
Conclusion
In this article we explained Python input and output operations in detail, including reading from standard input and writing to standard output or files. Properly handling input and output is essential for many Python applications. I recommend practising with your own examples to master these functions3.
Cheers for making it this far! I hope this journey through the programming universe has been as fascinating for you as it was for me to write down.
We’re keen to hear your thoughts, so don’t be shy – drop your comments, suggestions, and those bright ideas you’re bound to have.
Also, to delve deeper than these lines, take a stroll through the practical examples we’ve cooked up for you. You’ll find all the code and projects in our GitHub repository learn-software-engineering/examples-programming.
Thanks for being part of this learning community. Keep coding and exploring new territories in this captivating world of software!
References
McKinney, W. (2018). Python for data analysis: Data wrangling with Pandas, NumPy, and IPython. O’Reilly Media. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Lutz, M. (2013). Learning Python: Powerful Object-Oriented Programming. O’Reilly Media, Incorporated. ↩︎ ↩︎ ↩︎ ↩︎
Matthes, E. (2015). Python crash course: A hands-on, project-based introduction to programming. No Starch Press. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Downey, A. B. (2015). Think Python: How to think like a computer scientist. Needham, Massachusetts: Green Tea Press. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
3 - Flow Control
Conditions: making decisions in code
Life is full of decisions: “If it rains, I’ll take an umbrella. Otherwise, I’ll wear sunglasses.” These decisions are also present in the world of programming. Conditions are like questions the computer asks itself. They allow us to make decisions and execute specific code based on a condition1. They can be as simple as “Is it raining?” or as complex as “Is it the weekend and do I have less than $100 in my bank account?”.
if
The if
structure allows us to evaluate conditions and make decisions based on the result of that evaluation.
age = 15
if age >= 18:
print("You are an adult")
The code above allows executing a portion of code if a person’s age is greater than or equal to 18 years.
if-else
When you want to execute alternative code if the condition is false, you use the if-else
structure.
age = 21
if age >= 18:
print("You are an adult")
else:
print("You are a minor")
In this case, it determines if the person is an adult or a minor, and the message displayed is different.
if-elif-else
When conditions are multiple and two paths are not enough, the if-elif-else
structure is used to evaluate them in a chained way.
age = 5
if age <= 13:
print("You are a child")
elif age > 13 and age < 18:
print("You are a teenager")
else:
print("You are an adult")
In the code above, there are three clear paths: one for when age is less than or equal to 13, one for when age is between 13 and 18, and another for when age is greater than or equal to 18.
Another way to solve this problem is through the switch-case
structure, which, although Python does not natively incorporate, other languages like Java or C++ do, and it is an important tool to be familiar with. This structure allows programmers to handle multiple conditions in a more organized way than a series of if-elif-else
.
In Java, for example:
int day = 3;
switch(day) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
case 3:
System.out.println("Wednesday");
break;
// ... and so on
default:
System.out.println("Invalid day");
}
In the previous example, depending on the value of day
, the corresponding day will be printed2.
Loops: repeating actions
Sometimes in programming we need to repeat an action several times. Instead of writing the same code many times, we can use loops. These allow repeating the execution of a block of code while a condition is met3.
while
The while
loop is useful when we want to repeat an action based on a condition.
# Prints 1 to 5
i = 1
while i <= 5:
print(i)
i = i + 1
do-while
Similar to while
but guarantees at least one execution since the code block is executed first and then the condition is evaluated. Python does not implement this structure, but other languages like Java and C++ do.
int i = 1;
do {
System.out.println(i);
i++;
} while(i <= 5);
int number = 0;
do {
std::cout << "Hello, world!" << std::endl;
number++;
} while (number < 5);
for
The for
loop is useful when we know how many times we want to repeat an action.
for i in range(5):
print("Hello, world!")
The code above will print “Hello, world!” five times.
We can also iterate over the elements of a list or iterable object:
names = ["Maria", "Florencia", "Julian"]
for name in names:
print(f"Hello {name}")
# Prints
# Hello Maria
# Hello Florencia
# Hello Julian
The break
and continue
statements
We can use break
to terminate the loop and continue
to skip to the next iteration.
break
is used to completely terminate the loop when a condition is met, in the following example, when i
reaches 5.
# break example
i = 0
while i < 10:
print(i)
if i == 5:
break
i += 1
# Prints:
# 0
# 1
# 2
# 3
# 4
# 5
continue
is used to skip an iteration of the loop and continue with the next one when a condition is met. Here we use it to skip even numbers.
# continue example
i = 0
while i < 10:
i += 1
if i % 2 == 0:
continue
print(i)
# Prints:
# 1
# 3
# 5
# 7
# 9
Nesting: combining structures
Control flow structures can be nested within each other. For example, we can have loops within loops or conditions within loops.
for i in range(5):
for j in range(10):
if (i % 2 == 0 and j % 3 == 0):
print(f"i = {i}, j = {j}")
This code will print combinations of i
and j
only when i
is divisible by 2 and j
is divisible by 3, demonstrating how loops are nested and executed3.
Common usage patterns
There are specific patterns to solve common needs with control flow.
Search
Search for a value in a collection:
fruits = ["apple", "orange"]
searching = "orange"
found = False
for fruit in fruits:
if fruit == searching:
found = True
break
if found:
print("Fruit found!")
Accumulation
Accumulate incremental values in a loop:
total = 0
for i in range(10):
total += i
print(total) # Sum from 0..9 = 45
Flowcharts: the visual route to understanding code
Programmers, whether beginners or experts, often find themselves facing challenges that require detailed planning before diving into code. This is where flowcharts come into play as an essential tool. These charts are graphical representations of the processes and logic behind a program or system. In this article, we will unravel the world of flowcharts, from basic concepts to advanced techniques, and how they can benefit programmers of all levels.
A flowchart is a graphical representation of a process. It uses specific symbols to represent different types of instructions or actions. Its main purpose is to simplify understanding of a process by showing step by step how information or decisions flow. These charts:
- Facilitate understanding of complex processes.
- Aid in the design and planning phase of a program.
- Serve as documentation and reference for future developments.
Flowcharts are a powerful tool that not only benefits beginners but also experienced programmers. They provide a clear and structured view of a process or program, facilitating planning, design, and communication between team members.
Basic elements
Flowcharts consist of several symbols, each with a specific meaning:
- Oval: Represents the start or end of a process.
- Rectangle: Denotes an operation or instruction.
- Diamond: Indicates a decision based on a condition.
- Arrows: Show the direction of flow.
graph TD; start((Start)) process[Process] decision{Decision?} final((End)) start --> process; process --> decision; decision --> |Yes| process decision --> |No| final
Examples
Let’s design a flowchart for a program that asks for a number and tells us if it’s even or odd.
graph TB start((Start)) input[Input number] decision{Even?} isEven[Is even] isOdd[Is odd] final((End)) start --> input input --> decision decision --> |Yes| isEven decision --> |No| isOdd isEven --> final isOdd --> final
As programs become more complex, you may need to incorporate loops, multiple conditions, and other advanced elements into your flowchart. For example, here we diagram a program that sums numbers from 1 to a number entered by the user.
graph TD start((Start)) input[Input number] setVariables[Set sum=0 and counter=1] loop_condition{counter <= N?} loop_code[Add value and increment counter] result[Show sum] final((End)) start --> input input --> setVariables setVariables --> loop_condition loop_condition --> |Yes| loop_code loop_code --> loop_condition loop_condition --> |No| result result --> final
Conclusion
Control flow is the heart of programming. Without it, programs would be linear sequences of actions without the ability to make decisions or repeat tasks. By mastering these structures not only do you improve your ability to write code, but also your ability to think logically and solve complex problems.
Cheers for making it this far! I hope this journey through the programming universe has been as fascinating for you as it was for me to write down.
We’re keen to hear your thoughts, so don’t be shy – drop your comments, suggestions, and those bright ideas you’re bound to have.
Also, to delve deeper than these lines, take a stroll through the practical examples we’ve cooked up for you. You’ll find all the code and projects in our GitHub repository learn-software-engineering/examples-programming.
Thanks for being part of this learning community. Keep coding and exploring new territories in this captivating world of software!
References
Lutz, M. (2013). Learning Python: Powerful Object-Oriented Programming. O’Reilly Media, Incorporated. ↩︎
Deitel, P., & Deitel, H. (2012). Java: How to program. Upper Saddle River, NJ: Prentice Hall. ↩︎
Matthes, E. (2015). Python crash course: A hands-on, project-based introduction to programming. San Francisco, CA: No Starch Press. ↩︎ ↩︎
4 - Functions
What are functions?
A function, in simple terms, is a block of code that executes only when called. You can think of it as a small program within your main program, designed to perform a specific task1. A function can also be seen as a black box: we pass an input (parameters), some internal processing occurs, and it produces an output (return value).
Functions allow us to segment our code into logical parts where each part performs a single action. This provides several benefits2:
- Reusability: Once defined, we can execute (call) that code from anywhere in our program as many times as needed.
- Organization: It allows dividing a large program into smaller, more manageable parts.
- Encapsulation: Functions reduce complexity by hiding internal implementation details.
- Maintainability: If we need to make changes, we only have to modify the code in one place (the function) instead of tracking down all instances of that code.
Procedures vs. Functions
It is vital to distinguish between these two concepts. While a function always returns a value, a procedure performs a task but does not return anything. In some languages, this difference is clearer than in others. Python, for example, has functions that can optionally return values.
Anatomy of a function
In Python, a function is declared using the def
keyword, followed by the function name and parentheses. The code inside the function is called the body of the function3 and contains the set of instructions to execute to perform its task.
def my_function():
print("Hello from my function!")
To call or invoke a function, we simply use its name followed by parentheses:
my_function() # Output: Hello from my function!
Parameters and arguments
Functions become even more powerful when we pass information to them, known as parameters. These act as “variables” inside the function, allowing the function to work with different data each time it is called.
While parameters are variables defined in the function definition, arguments are the actual values passed when calling the function.
def greet(name):
print(f"Hello {name}!")
greet("Maria")
# Output:
# Hello Maria!
Python allows default parameters, which have a predetermined value, making passing those arguments optional when calling the function. It also allows named parameters which enable passing arguments in any order by specifying their name.
def greet(name="Maria", repetitions=3):
repetition = 1
while repetition <= repetitions:
print(f"Hello {name}!")
repetition += 1
greet()
# Output:
# Hello Maria!
# Hello Maria!
# Hello Maria!
greet("Florencia", 4)
# Output:
# Hello Florencia!
# Hello Florencia!
# Hello Florencia!
# Hello Florencia!
greet(repetitions=2, name="Julian")
# Output
# Hello Julian!
# Hello Julian!
Returning values
Functions can return a result or return value using the return
keyword.
def circle_area(radius):
return 3.14 * (radius ** 2)
result = circle_area(10)
print(result) # Output: 314
The return value is passed back to where the function was called and can be assigned to a variable for later use.
Functions can also perform some task without explicitly returning anything. In Python this is known as returning None
.
Local and global variables
Local variables are defined inside a function and only exist in that scope, while global variables are defined outside and can be accessed from anywhere in the code. It is crucial to understand their scope (where a variable is accessible) and lifetime (how long a variable lives).
x = 10 # x is global
def add():
y = 5 # y is local
return x + y
add() # Output: 15
print(y) # Error, y does not exist outside the function
We can read global variables from a function, but if we need to modify it we must declare it global
.
x = 10
def add():
global x
x = x + 5
add()
print(x) # 15
Best Practices
When creating functions we should follow certain principles and patterns4:
- The name of a function should clearly indicate its purpose.
- Make functions small, simple, and focused on one task. A function should do one thing and do it well.
- Use descriptive names for functions and parameters.
- Avoid side effects and modifying global variables.
- Properly document the purpose and usage of each function.
- Limit the number of parameters, ideally 0 to 3 parameters.
Following these best practices will help us create reusable, encapsulated, and maintainable functions.
Conclusion
Functions are core components in programming, allowing us to organize, reuse, and encapsulate code. By defining functions that perform a single task we keep our programs simplified, easy to understand, and modify. By understanding and mastering this concept, you not only improve the quality of your code but also your efficiency as a developer.
Cheers for making it this far! I hope this journey through the programming universe has been as fascinating for you as it was for me to write down.
We’re keen to hear your thoughts, so don’t be shy – drop your comments, suggestions, and those bright ideas you’re bound to have.
Also, to delve deeper than these lines, take a stroll through the practical examples we’ve cooked up for you. You’ll find all the code and projects in our GitHub repository learn-software-engineering/examples-programming.
Thanks for being part of this learning community. Keep coding and exploring new territories in this captivating world of software!
References
McConnell, S. (2004). Code Complete. Microsoft Press. ↩︎
Joyanes Aguilar, L. (2008). Fundamentos de programación: algoritmos, estructura de datos y objetos. McGraw-Hill. ↩︎
Python Software Foundation. (2022). Python Official Documentation. ↩︎
Kindler, E., & Krivy, I. (2011). Object-Oriented Simulation of systems with Java: A working introduction. BoD–Books on Demand. ↩︎
5 - Recursive Functions
Recursion: the art of calling yourself
Imagine a box of mirrors where each mirror reflects what it sees in the next, creating an infinite series of reflections. Recursion in programming is something similar. It is a technique where a function calls itself directly or indirectly1. This creates a cycle where the function solves a problem by dividing it into smaller instances of the same problem, until reaching a simple base case that can be solved directly.
For example, let’s imagine a function that prints a countdown:
def countdown(number):
if number > 0:
print(number)
countdown(number - 1)
else:
print("Blastoff!")
countdown(5)
This function calls itself recursively decrementing the number each time until reaching 0, and then prints the blastoff message.
Recursion is a declarative approach that focuses on dividing a problem into recursive cases without needing to explicitly control the loop using iterators or counters like in imperative programming.
The structure of a recursive function
The power of recursion lies in its simplicity. However, it is essential to understand its structure to avoid common pitfalls. A typical recursive function has two main parts2:
- Base case: The simplest case with a known solution that doesn’t require recursion. It is the stopping condition that halts the recursion. Without a base case, we would fall into infinite recursion which eventually overflows the call stack.
- Recursive case: This is where the magical recursive call occurs. At this point, the function calls itself with a modified argument, usually a reduced version of the original problem.
Classic recursion examples
Factorial
The factorial of a positive integer \(n\) is the product of all positive integers less than or equal to \(n\). For example:
- \(5! = 5 * 4 * 3 * 2 * 1 = 120\)
- \(4! = 4 * 3 * 2 * 1 = 24\)
- \(3! = 3 * 2 * 1 = 6\)
Here is the Python code for calculating factorial using recursion:
def factorial(n):
if n == 1:
return 1 # Base case
return n * factorial(n-1) # Recursive case
- Base case: The base case is the simplest, smallest instance of the problem that can be answered directly. For factorial, when \(n = 1\), the result is \(1\).
- Recursive case: If \(n\) is greater than \(1\), the function calls itself with \(n-1\), and multiplies the result by \(n\).
Let’s say you want to calculate the factorial of \(5\), so you call factorial(5)
.
Here is what happens:
- Step 1: Since \(n = 5\) is not \(1\), the function calls
factorial(4)
, then multiplies the result by \(5\). - Step 2: Now, inside
factorial(4)
, \(n = 4\), so the function callsfactorial(3)
, then multiplies the result by \(4\). - Step 3: Inside
factorial(3)
, \(n = 3\), so it callsfactorial(2)
, then multiplies the result by \(3\). - Step 4: Inside
factorial(2)
, \(n = 2\), so it callsfactorial(1)
, then multiplies the result by \(2\). - Step 5: Finally,
factorial(1)
reaches the base case, where \(n = 1\), so it returns \(1\).
Now the results unwind:
factorial(2)
returns \(2 * 1 = 2\)factorial(3)
returns \(3 * 2 = 6\)factorial(4)
returns \(4 * 6 = 24\)factorial(5)
returns \(5 * 24 = 120\)
The final result is \(120\), which is the value of \(5!\).
Here is a visual representation of the call stack:
factorial(5)
-> factorial(4)
-> factorial(3)
-> factorial(2)
-> factorial(1)
return 1
return 2
return 6
return 24
return 120
Fibonacci sequence
The Fibonacci sequence is a series of numbers where each number is the sum of the previous two. It starts with \(0\) and \(1\), and each subsequent number is the sum of the two numbers before it. The beginning of the sequence is: \(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …\)
Here is the Python code for calculating the \(n^th\) Fibonacci number using tail recursion:
def fibonacci(n, a=0, b=1):
if n == 0:
return a
return fibonacci(n-1, b, a+b)
The function takes three parameters:
- \(n\): The position of the desired number in the sequence.
- \(a\) and \(b\): Two numbers that aid in the sequence calculation.
Here is a breakdown of how the function works:
- Base case: If \(n\) is \(0\), the function returns \(a\). This is the value of the \(n^th\) number in the sequence.
- Recursive case: If \(n\) is not \(0\), the function calls itself with \(n-1\), \(b\), and \(a+b\). These parameters change the position in the sequence and prepare the next numbers for summation.
Suppose we want to find the \(5^th\) number in the Fibonacci sequence by calling fibonacci(5)
.
Here is what happens:
- Step 1: Since \(n = 5\), it calls
fibonacci(4, 1, 1)
(because \(a = 0\), \(b = 1\), \(a + b = 1\)). - Step 2: Since \(n = 4\), it calls
fibonacci(3, 1, 2)
(because \(a = 1\), \(b = 1\), \(a + b = 2\)). - Step 3: Since \(n = 3\), it calls
fibonacci(2, 2, 3)
(because \(a = 1\), \(b = 2\), \(a + b = 3\)). - Step 4: Since \(n = 2\), it calls
fibonacci(1, 3, 5)
(because \(a = 2\), \(b = 3\), \(a + b = 5\)). - Step 5: Since \(n = 1\), it calls
fibonacci(0, 5, 8)
(because \(a = 3\), \(b = 5\), \(a + b = 8\)). - Step 6: Since \(n = 0\), it returns \(a\), which is \(5\).
The result is \(5\), which is the \(5^th\) number in the Fibonacci sequence.
Here is a visual representation of the call stack:
fibonacci(5, 0, 1)
-> fibonacci(4, 1, 1)
-> fibonacci(3, 1, 2)
-> fibonacci(2, 2, 3)
-> fibonacci(1, 3, 5)
-> fibonacci(0, 5, 8)
return 5
Advantages and Disadvantages
Recursion has certain advantages3:
- It can result in simple, elegant solutions for problems that easily break down into subproblems.
- It eliminates the need for explicit loop control.
- It mirrors the mathematical structure of a recursive definition.
The disadvantages include:
- It can be less efficient (high memory consumption) than iteration due to repeated function calls and stack frame creation.
- Too much recursion can overflow the call stack and cause crashes.
- It can be harder to debug and analyze than iteration.
Therefore, recursion is a powerful tool that should be used judiciously in appropriate cases.
Recursion vs Iteration
Recursion and iteration (using loops) are parallel tools and we can use either one to solve many problems. Both techniques have the potential to solve the same problems, but their implementation and efficiency may vary. Let’s take the factorial example:
Iterative
def factorial_iterative(n):
result = 1
for i in range(1, n+1):
result *= i
return result
Recursive
def factorial_recursive(n):
if n == 1:
return 1
return n * factorial(n-1)
The iterative version is more efficient in terms of space, but the recursive is cleaner and easier to understand. The choice between recursion and iteration often depends on the specific problem, memory constraints, and programmer preferences.
Conclusion
Recursion is a key technique that allows writing elegant, natural, and efficient algorithms when properly leveraged. Understanding how to break down a problem into recursive cases is essential to master this skill. Recursion provides a unique declarative alternative to solve complex problems without managing explicit loops. However, it is crucial to always remember to define an adequate base case and be aware of recursion limitations in terms of efficiency and memory usage. Knowing how to combine recursion and iteration gives flexibility when creating optimal solutions.
As always, the key lies in finding the right balance and using the right tool for the right job.
Cheers for making it this far! I hope this journey through the programming universe has been as fascinating for you as it was for me to write down.
We’re keen to hear your thoughts, so don’t be shy – drop your comments, suggestions, and those bright ideas you’re bound to have.
Also, to delve deeper than these lines, take a stroll through the practical examples we’ve cooked up for you. You’ll find all the code and projects in our GitHub repository learn-software-engineering/examples-programming.
Thanks for being part of this learning community. Keep coding and exploring new territories in this captivating world of software!
References
Cormen, T.H., Leiserson, C.E., Rivest, R.L., & Stein, C. (2009). Introduction to Algorithms. MIT Press. ↩︎
Kindler, E., & Krivy, I. (2011). Object-Oriented Simulation of systems with Java: A working introduction. BoD–Books on Demand. ↩︎
Lutz, M. (2013). Learning Python: Powerful Object-Oriented Programming. O’Reilly Media, Incorporated. ↩︎