CMP 202
CMP 202
LECTURE SERIES
OVERVIEW OF THE COURSE OUTLINE
➢Principles of Good programming
➢Structured programming concepts
➢Errors and Debugging
➢Testing
➢Text Files and IO
Principles of Good Programming
• There are certain universal laws and principles in software development
that guide architects, programmers, and anyone needing to design a
working software. These slides lists quite a few of those principles,
although it's far from complete.
• Programming is difficult (like many activities that are useful and
worthwhile—and like most of those activities, it can also be rewarding and
a lot of fun).
• When you write a program, you have to tell the computer every small
detail of what to do. And you have to get everything exactly right, since the
computer will blindly follow your program exactly as written.
• How, then, do people write any but the most simple programs? It’s not a
big mystery, actually. It’s a matter of learning to think in the right way.
How to start
• A program is an expression of an idea.
• A programmer starts with a general idea of a task for the computer to
perform.
• Presumably, the programmer has some idea of how to perform the task by
hand, at least in general outline.
• Programming in the small, which is sometimes called coding, would
then refer to filling in the details of that design.
• The details are the explicit, step-by-step instructions for performing fairly
small-scale tasks.
• When you do coding, you are working fairly “close to the machine,”
with some of the same concepts that you might use in machine
language:
• memory locations, arithmetic operations, loops and branches
What are Programming principles?
• Programming is the process of coding, testing, troubleshooting,
debugging and maintaining a system. Programming principles help
you to write excellent quality of code and maintain a good coding
practice.
• A personal Recount:
• Programming principles have helped me over the years in becoming a better
programmer, and I believe, this will help any developer become more
efficient and able to produce code which is easier to maintain.
• By following these coding principles, you can save development and
maintenance time, and conquer lots of other bottlenecks which generally
arise in later development phases.
Why should a developer follow the principles?
• When I started my adventure with programming in Java, I wrote
complete spaghetti code.
• It means each file had many responsibilities, no abstraction, and it was
difficult to debug and fix issues.
• I am not only one person working on applications; my team is working on the
same. Later, I started thinking how to improve quality of the code. And the
answer is Coding Principles.
• Actually, writing programs is often a matter of personal taste but
there are certain principles or guidelines that should be followed
within your own style in order to make programs easy to maintain
and understand by both, you and others, and also principles guide the
creation of stable, scalable, and robust code.
What are the benefits if developer followed it?
• Readability
All developers were in a team able to understand the code.
• Extensibility
In the software world, we should not blame the change requests;
it’ll come at any time. So our code is always easy to extend without
breaking existing business rules.
• Maintainability
It’s easy to maintain by the development team and the
production support team too because the application is loosely
coupled.
• Modularity
Divide a program into reusable pieces: functions, modules,
libraries.
Keep things DRY
• DRY means Don’t-Repeat-Yourself
DRY is also known as Duplication is Evil (DIE) or Once And Only Once.
Every piece of knowledge must have a single, unambiguous, authoritative
representation within a system.
---DRY
• Each significant piece of functionality in a program should be implemented in
just one place in the source code. Where similar functions are carried out by
distinct pieces of code, it is generally beneficial to combine them into one by
abstracting out the varying parts.
• Why
• Duplication (inadvertent or purposeful duplication) can lead to maintenance
nightmares, poor factoring, and logical contradictions.
• A modification of any single element of a system does not require a change in
other logically unrelated elements.
• Additionally, elements that are logically related all change predictably and
uniformly, and are thus kept in sync.
• How
• Put business rules, long expressions, if statements, math formulas, metadata,
etc. in only one place.
• Identify the single, definitive source of every piece of knowledge used in
your system, and then use that source to generate applicable instances of that
You’re NOT Gonna Need It!
• YAGNI stands for "you aren't gonna need it": don't implement something
until it is necessary.
• As developers, we'll always think a lot about the future usability of the
project and try to do some extra features coding in a mind that “just in case
we need them” or “we will eventually need them”. Just one word… Wrong!
I’ll repeat it this way: You didn’t need it, you don’t need it, and in most of
the cases… “You Aren’t Gonna Need It”.
Why
• Any work that's only used for a feature that's needed tomorrow, means losing
effort from features that need to be done for the current iteration.
• It leads to code bloat; the software becomes larger and more complicated.
• How
• Always implement things when you actually need them, never when you just
foresee that you need them.
KISS Principle - Keep It Simple, Stupid
• Nowadays programming languages, frameworks, and APIs have
powerful means to create sophisticated solutions for various kinds of
problems. Sometimes developers might feel tempted to write
“clever” solutions that use all these complex features.
The KISS principle states that most systems work best if they are kept
simple rather than making them complex; therefore simplicity should
be a key goal in design and unnecessary complexity should be
avoided.
• The other control structures are the case, do-until, do-while, and for
are not needed. However, they are sometimes convenient and are
usually regarded as part of structured programming. In assembly
language, they add little convenience.
Steps in a Programming Project
• In CMP201, the importance of a clear, structured programming style
has been emphasized. Because of the importance of these principles,
we will dive much into this concepts as it is worth reviewing them
here before we continue with the main part of CMP202.
• Any programming project, no matter how simple, should be divided
into several main sections:
1. Make sure you understand the problem you are trying to solve. This
may seem obvious, but many later problems in coding can be traced
back to your lack of a complete understanding of just what it is the
program is supposed to do
Steps in a Programming Project
• 2. Once you have a clear picture in your mind of the problem, you should
write out the specifications that the program is to satisfy.
• As a simple example, suppose you wish to write a program that sorts a list
of numbers into ascending order. Even for such a simple program, you need
to consider the specifications before you put finger to keyboard.
• For example, how are the numbers to be input into the program?
• Will they be typed in from the keyboard or read from a file?
• Will the quantity of numbers be known in advance, or should this quantity be
requested from the user before the numbers are read, or should the program count
the numbers as they are read in?
• Where is the sorted list to be output (on screen or in a file)?
• If files are used for input and/or output, are the filenames to be constant,
or should the program request the filenames from the user?
• Should the program be able to sort integers or real numbers as well?
Steps in a Programming Project
• You should have made up your mind on all these points before you
even sit down in front of the computer.
• Failing to think things through at this stage can mean a lot of
chopping and changing in the program later, which leads to jumbled
code that is full of errors, and difficult to debug, as well as a lot of
wasted time.
In the above code the closing curly bracket of the main() method is
missing.
Example of Syntax Error: Misspelled Keyword
Consider the following code.
• In the above code, we have not imported the java.util.Scanner package and
using Scanner to take integer input. This has caused the error.
Example of Syntax Error: Missing double-quote in String
Consider the following code.
• In the above code the Java compiler is throwing multiple errors because it
is not considering the "Hello World!" as a String as it is missing the double
quotes.
RUNTIME ERRORS
• Runtime errors occur when the program has successfully compiled without
giving any errors and creating a ".class" file.
• However, the program does not execute properly. These errors are
detected at runtime or at the time of execution of the program.
• The Java compiler does not detect runtime errors because the Java
compiler does not have any technique to catch these errors as it does not
have all the runtime information available to it. Runtime errors are caught
by Java Virtual Machine (JVM) when the program is running.
• These runtime errors are called exceptions and they terminate the
program abnormally, giving an error statement.
• Exceptions are errors thrown at runtime.
• We can use exception handling techniques in Java to handle these runtime errors.
• In exception handling, the piece of code the programmer thinks can produce the
error is put inside the try block and the programmer can catch the error inside
the catch block.
What causes Run time Errors?
• A runtime error can happen when:
• Dividing an integer by zero.
• Trying to store a value into an array that is not compatible type.
• Trying to access an element that is out of range of the array.
• Passing an argument that is not in a valid range or valid value for a method.
• Striving to use a negative size for an array.
• Attempting to convert an invalid string into a number.
Example of Run time error: Accessing array index that
does not exists
• Consider the following code:
• Java provides strong but flexible support for I/O related to files.
• The Files can be classified into two major categories: Binary files and Text
files.
• A binary file is a file whose contents must be handled as a sequence of binary digits.
• A text file is a file whose contents are to be handled as a sequence of characters.
• Why use files for I/O?
• Files provide permanent storage of data.
• Files provide a convenient way to deal with large quantities of data.
What are Standard Streams?
• All the programming languages provide support for standard I/O where the
user's program can take input from a keyboard and then produce an output
on the computer screen.
• Recall that, in Java the I/O is handled by objects called streams. Java
provides the following three standard streams as described as follows:
• Standard Input: This is used to feed the data to user's program and usually
a keyboard is used as standard input stream and represented as System.in.
• Standard Output: This is used to output the data produced by the user's
program and usually a computer screen is used for standard output stream
and represented as System.out.
• Standard Error: This is used to output the error data produced by the
user's program and usually a computer screen is used for standard error
stream and represented as System.err.
Java BufferedReader Class
• Java BufferedReader class is used to read the text from a character-
based input stream.
• It can be used to read data line by line by readLine() method.
• It makes the performance fast. It inherits Reader class.
• Let's see the declaration for Java.io.BufferedReader class:
public class BufferedReader extends Reader
Java BufferedReader class constructors
• The following table summarizes the constructors and their description
Java BufferedReader class methods
Opening a text file for reading
• A stream of the class BufferedReader is created and connected to a text file
for reading as follows:
BufferedReader streamName = new BufferedReader(new FileReader(filename));
• Where, filename is a File object or a constant string or a String variable
containing the name or the full path of the file to be read.
• Example of valid filenames:
“myinput.txt”,“C:\\homework\\StudentTest.java”, “C:/homework/StudentTest.java”
• Each time you create a string literal, the JVM checks the "string
constant pool" first. If the string already exists in the pool, a reference
to the pooled instance is returned. If the string doesn't exist in the
pool, a new string instance is created and placed in the pool. For
example:
String s1="Welcome";
• Java String can also be created by using the new operator as in:
String greeting = new String(“Salaam Shabaab”);
//creates two objects and one reference variable
• In such case, JVM will create a new string object in normal (non-pool) heap
memory, and the literal "Salaam Shabaab" will be placed in the string constant
pool. The variable greeting will refer to the object in a heap (non-pool).
Internal Representation of Strings
• Internally, String objects are represented as a sequence of characters
indexed from 0. For example, the string object created by the statement:
String greeting = “Salaam Shabaab”; is represented as follows:
• Example: This program returns a string whereby any leading and trailing
whitespaces will be removed.
Replace method in Strings
• Another useful String method is also replace() which replaces occurrences of
character with a specified new character.
The fact that Strings are immutable makes string processing very efficiently in
Java.
More String Methods
We have discussed some of the most important methods of the string class.
For a complete list, check the Java SDK documentation.
Usmanu Danfodiyo University, Sokoto
Department of Computer Science
SEARCHING
1
CONTENTS
✓ What is Searching?
✓ Types of Searching
▪ Linear Search
▪ Binary Search
2
WHAT IS SEARCHING?
3
SEARCHING
❖ We know that today’s computers store a lot of information.
❖ To retrieve this information proficiently we need a very efficient way of
doing it and to make sure we are retrieving the right information.
7
LINEAR SEARCH
✓ Linear or Sequential Search is the simplest of search algorithms.
✓ Linear Search has no pre-requisites for the state of the underlying data
structure.
data structure until either the element is found or the end of the structure is
reached.
✓ If the element is found, we usually just return its position in the data
structure.
9
LINEAR SEARCH (Example)
public static int linearSearch(int arr[], int elementToSearch) {
if (arr[index] == elementToSearch)
return index;
return -1;
}
10
LINEAR SEARCH (Example)...
To test it, we'll use a simple Array of integers: Here, we are searching for “67” in the below array
89 57 91 47 95 3 27 22 67 99
✓ Phonebook Search: Linear search can be used to search through a phonebook to find a
person’s name, given their phone number.
✓ Spell Checkers: The algorithm compares each word in the document to a dictionary of
correctly spelled words until a match is found.
✓ Finding Minimum and Maximum Values: Linear search can be used to find the minimum
and maximum values in an array or list.
✓ Searching through unsorted data: Linear search is useful for searching through unsorted
data.
14
ADVANTAGES OF LINEAR SEARCH
✓ Simplicity: Linear search is a very simple algorithm to understand and
implement.
✓ Works with unsorted data: Linear search works well with unsorted data. It
does not require any pre-processing or sorting of the data before
performing the search.
✓ Low memory usage: Linear search only requires a small amount of memory
to store the index of the current element being searched.
✓ Easy to debug: Because linear search is a simple algorithm, it is easy to
debug and troubleshoot any issues that may arise.
15
DISADVANTAGES OF LINEAR SEARCH
✓ Inefficient for large datasets: As the size of the dataset grows, the time
taken by linear search also increases proportionally.
✓ Limited applicability: Linear search is only suitable for datasets that are not
too large or not too complex.
✓ No early termination: Linear search does not have a mechanism to
terminate early once the target element is found.
✓ Inefficient for sorted data: When the data is already sorted, linear search is
not efficient because it needs to check each element one by one, even if
it has already passed the target element.
16
BINARY SEARCH
17
BINARY SEARCH
❖ Binary or Logarithmic Search is one of the most commonly used search
algorithms primarily due to its quick search time.
❖ This kind of search uses the Divide and Conquer methodology and requires
the data set to be sorted beforehand.
❖ If data is not sorted, then it to be sorted or we use Linear Search
❖ It divides the input collection into equal halves, and with each iteration
compares the target element with the element in the middle.
18
BINARY SEARCH (CONT)....
❖ If the element is found, the search ends.
❖ Else, we continue looking for the element by dividing and selecting the
appropriate partition of the array, based on if the target element is
smaller or bigger than the middle element.
19
BINARY SEARCH (CONT)....
Below are the steps:
✓ Else If x is greater than the mid element, then x can only lie in the right half
subarray after the mid element. So we recur for right half.
20
BINARY SEARCH (Example)...
Let’s use the below array
3 11 21 29 41 54 61 78 110 127
54 61 78 110 127
21
BINARY SEARCH (Example)...
public static int binarySearch(int arr[], int firstElement, int lastElement,
int elementToSearch) {
// termination condition
if (arr[mid] == elementToSearch)
return mid;
3 22 27 47 57 67 89 91 95 99
22
BINARY SEARCH (Example)...
// if the middle element is bigger than the goal element
return -1;
Output:
} 67 found at index: 5
We can use this algorithm like this:
int index = binarySearch(new int[]{3, 22, 27, 47, 57, 67, 89, 91, 95, 99}
23
ADVANTAGES OF BINARY SEARCH
24
DISADVANTAGES OF BINARY SEARCH
✓ Small unsorted arrays would take considerate time in sorting and then
searching the desired element.
25
APPLICATIONS OF BINARY SEARCH
❖ It is the most commonly used search algorithm in most of the libraries for searching.
❖ The Binary Search tree is used by many data structures as well which store sorted data.
26
Usmanu Danfodiyo University, Sokoto
Department of Computer Science
SORTING
1
CONTENTS
❖ What is Sorting?
2
WHAT IS SORTING?
3
Introduction to Sorting
❖ While getting into the world of programming, many problems will include
working with multiple instances of similar types of data.
❖ Generally, these data will take the help of arrays for their storage.
❖ The bigger question is how to optimize the solution for a particular problem
to fit within a particular amount of time.
❖ The generalized answer is arranging the elements within the array in such a
way that an order is maintained.
❖ This is the process of sorting, used in many programming languages like
Java, Python, and other high-level programming languages.
4
Introduction to Sorting (Cont)…
❖ Sorting is a class of algorithms that are tasked with rearranging the positions of elements of
an array such that all of its elements are either in ascending or descending order.
❖ A good sorting algorithm also needs to ensure that elements having the same value don’t
change their locations in the sorted array.
❖ Sorting is necessary for getting a concrete understanding of data structures and
algorithms.
❖ There are different types of sorting algorithms which include:
✓ Bubble Sort
✓ Selection Sort
✓ Merge Sort
✓ Heap Sort
✓ Insertion Sort
5
BUBBLE SORT
6
Bubble Sort
❖ It is also called sinking sort
❖ While applying this sorting algorithm on an unsorted array, the largest element tends to sink
at the end of the array.
❖ It repeatedly compares a pair of adjacent elements and swaps them if they are in the
wrong order.
3 1 5 2 6 4
❖ First, compare 3 and 1, if the leftmost element is greater than the rightmost element, then
we swap the elements otherwise we will not swap.
❖ In this case, 3 is greater than 1, we then swap and the array will be:
1 3 5 2 6 4
7
Bubble Sort (Cont…)
❖ Then, we compare 3 and 5, if the leftmost element is greater than the rightmost element,
then we swap the elements otherwise we will not swap.
1 3 5 2 6 4
❖ The process will be repeated until we reach the end of 1st Pass and we have the array:
1 3 2 5 4 6
❖ Then we start the 2nd pass by comparing 1 and 3, then 3 and 2 ………
1 2 3 5 4 6
❖ Compare 3 and 5, there will be no swap…. Then compare 5 and 4, there will be a swap
1 2 3 4 5 6
❖ Then we move to the 3rd pass and compare 1 and 2, 2 and 3, 3 and 4 ………
❖ An array becomes sorted when no swap occurs in a given pass.
❖ In this case, after the second pass, we already have a sorted array because all elements
are in the correct position. 8
Bubble Sort (Cont…)
❖ Consider the array: 5 1 9 2 10 n = 5;
boolean isSwapped;
❖ isSwapped is a Boolean variable that keeps track to know if a
for (int i = 0; i < n-1; i++;
swap is done in a given iteration isSwapped = false;
for(int j=0; j<n-1-i; j++;
❖ j at line 4 will be 1 (index 1) at the first iteration.
if (arr[j] > arr[j+1]){
❖ After the first swap, it will be incremented to 2 (index 2). int temp = arr[j];
arr[j] = arr[j+1];
❖ j is incremented at the end of every iteration
arr[j+1] = temp;
❖ temp is used to store the element that will be moved from the isSwapped = true;
}
leftmost side, in this case, temp will hold 5 and after 1 has
}
been swapped into index 0, then the element in temp will be If (isSwapped == false)
break;
swapped to index 1
}
❖ The for loop exits when j=4 and that marks the end of 1st pass.
1 5 2 9 10
1 2 5 9 10
❖ Line 9 is true, meaning the array is not sorted.
❖ Once line 12 is true, then the array is sorted.
9
Bubble Sort (Java Implementation)
❖ The below code shows implementation of bubble sort in Java programming
public class Bubblesort{
public void printArray(int[] arr) { if (isSwapped ==false){
int n = arr.length; break;
for (int i=0; i<n; i++) { }
System.out.print(arr[i] + " "); }
} }
System.out.println();
} public static void main (String[] args) {
int[] arr = new int[] {5, 1, 2, 9, 10};
public void sort(int[] arr){ BubbleSort bs = newBubbleSort();
int n = arr.length; bs.printArray(arr);
boolean isSwapped; bs.sort(arr);
bs.printArray(arr);
for (i=0; i < n-1; i++){ }
isSwapped = false; }
for (int j=0; j < n-1-i; j++){
//compare adjacent elements
if (arr[j] > arr[j+1]){
//create a temp location to swap element at index j
int temp = arr[j]; Output:
arr[j] = arr[j+1];
arr[j+1] = temp;
5 1 2 9 10
isSwapped = true;
} 1 2 5 9 10
}
10
SELECTION SORT
11
Selection Sort
❖ In selection sort, we divide the given array into two parts – sorted and unsorted part.
❖ The algorithm sorts an array by repeatedly finding the minimum in an unsorted array and
making it part of the sorted array.
❖ From unsorted part, we pick minimum element and swap it with leftmost element of
unsorted part.
❖ After swap, the element now becomes part of the sorted array.
0 1 2 3 4
3 1 5 2 6
Sorted Unsorted
❖ It repeated till unsorted part is empty and all element are moved to the sorted part.
0
1 3 5 2 6
Sorted Unsorted
12
Selection Sort (Example)
❖ Lets use Selection sort to sort the below array
0 1 2 3 4 5 0 1 2 3 4 5
3 1 5 2 6 4 1 3 5 2 6 4
Sorted Unsorted
❖ After the 1st Pass, 1 is found to be the smallest element, then we swap and move 1 to the
unsorted part. 0
1 3 5 2 6 4
Sorted Unsorted
❖ Then, we move to the 2nd Pass, we found 2 to be the smallest, we swap it with the first
element in the unsorted part: 2 5 3 6 4
❖ We then move 2 to the sorted part of the array
0 1
1 2 5 3 6 4
Sorted Unsorted 13
Selection Sort (Example…)
❖ We move to the 3rd pass, we have the below after the pass
1 2 3 5 6 4
Sorted Unsorted
❖ Then we move to the 4th Pass, then we will have the below array
1 2 3 4 6 5
Sorted Unsorted
❖ At the end of the 5th Pass, we will have:
1 2 3 4 5 6
Sorted Unsorted
❖ Since, we only have one element left in the unsorted part, it means all elements are in their
correct position.
1 2 3 4 5 6
14
Selection Sort (Cont…)
❖ Consider the array: 5 1 10 2 9 n = 5;
Public void sort(int[]arr){
int n = arr.length;
❖ After 1st Pass 1 5 10 2 9
for(int i=0; i<n-1; i++){
int min = i;
for(int j=i+1; j<n; j++){
❖ After 2nd Pass 1 2 10 5 9 if(arr[j] < arr[min]){
min = j;
}
1 2 5 10 9 }
❖ After 3rd Pass
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
❖ After the 4th Pass 1 2 5 9 10
}
}
16
END OF SORTING
17
Usmanu Danfodiyo University, Sokoto
Department of Computer Science
RECURSION
1
CONTENTS
❖ What is Recursion?
❖ Examples
❖ Halting Condition
2
WHAT IS RECURSION?
3
Introduction to Recursion
❖ A process in which a function calls itself directly or indirectly is
called recursion and the corresponding function is called a
recursive function.
✓ Some problems are inherently recursive like tree traversals, Tower of Hanoi, etc. For such
problems, it is preferred to write recursive code.
❖ Disadvantages
✓ The recursive program has greater space requirements than the iterative program as
all functions will remain in the stack until the base case is reached.
✓ It also has greater time requirements because of function calls and returns overhead.
Note: Both recursive and iterative programs have the same problem-solving powers, i.e.,
every recursive program can be written iteratively and vice versa is also true.
10
SNAP TEST
11
Snap Test
Write a recursive method to get the factorial of 3 and print the output
12
Solution
public class Factorial{
public static void main (String [] args){
System.out.println(factorial(3));
}
private static int factorial(int n){
if (n==1){
System.out.println(“factorial(“+ n +”) = 1”);
return 1;
}
else {
System.out.println(“factorial(“+ n +”) = “ + n +” * factorial(“ + (n-1) +”)”);
return n* factorial(n-1);
}
}
}
OUTPUT
Factorial(3) = 3 * factorial(2)
Factorial(2) = 2 * factorial(1)
Factorial(1) = 1
6
13