<jupyter_start><jupyter_text>**Boolearns**Anothertype in Python is the **Boolean** type. There are two boolearn values:- **True**- **False**.They can be created by **comparing values**, for instance by using the equal operator **==**.> my_booleran = True> print(my_boolean)> True> print(2==3)> False> print("hello" == "hello")> TrueBe careful not to confuse assignment **"="** (one equals sign) with comparison **"=="** (two equals signs). **Comparision** Another comparison operator, the **not equal** operator **(!=)**, evaluates to **True** if the items being compared **aren't equal**, and False if they are.> print(1 != 1)> False > print("eleven" != "seven")> True> print(2 != 10)> TrueComparison operators are also called **Relational operators.**<jupyter_code>print(7 != 8)<jupyter_output>True<jupyter_text>**Comparison**Python also has operators that determine whether one number (float or integer) is - greater than  **">"**- smaller than  **"<"**> print( 7 > 5 )> True> print( 10 < 10 )> FalseDifferent numeric types can also be compared, for example, integer and float.<jupyter_code>print( 7 > 7.0)<jupyter_output>False<jupyter_text>**Comparision**- The greater than or equal to **">="**- The smaller than or equal to **"<="**They are the same as the strict greater than and smaller than operators, except that they return **True** when **comparing equal numbers**.> print(7 <= 8)> True> print(9 >= 9.0)> TrueGreater than and smaller than operators can also be used to compare strings **lexicographically** (The alphabetical order of words is based on the alphabetical order of their componenet letters).**For example:**> print("Annie" > "Andy")> TrueThe first two characters from "Annie" and "Andy" (A and A) are compared. As they are equal, the second two characters are compared. Because they are also equal, the thrid two characters (n and d) are compared. And because n has greater alphabetical order value than d, "Annie" is greater than "Andy".<jupyter_code>print(8.7 <= 8.70)<jupyter_output>True<jupyter_text>**if Statements**You can use if statements to run code if a certain condition holds. if an expression evaluates to True, some statements are carried out. Otherwise, they aren't carried out.An if statement looks like this:> if expression: >> statementsPython uses indentation (white space at the beginning of a line) to delimit blocks of code. Depending on program's logic, indentation can be mandatory. As you can see, the statements in the if should be indented. **if Statements**Here is an example if statement:> if 10 > 5:>> print("10 greater than 5")> print("Program ended")The expression determines whether 10 is greater than 5. Since it is, the indented statement runs, and "10 greater than 5" is output. Then, the unindented statement, which is not part of the if statement, is run, and "Program ended" is displayed.Notice the **colon** at the end of the expression in the if statement.<jupyter_code>spam = 7 
if spam > 5:
  print("five")
if spam > 8:
  print("eight")<jupyter_output>five<jupyter_text>**if Statements**To perform more complex checks, if statements can be nested, one inside the other. This means that the inner if statement is the statement part of the outer one. This is one way to see whether multiple conditions are satisfied.For example:> num = 12> if num > 5:>> print("Bigger than 5")>> if num <= 47:>>>print("Between 5 and 47")**Indentation** is used to define the level of nesting.<jupyter_code>num = 7
if num > 3:
  print("3")
  if num < 5:
    print("5")
    if num == 7:
      print("7")<jupyter_output>3<jupyter_text>**else Statements**The **if statemen**t allows you to check a condition and run some statements, if the **condition is True**.The **else statement** can be used to run some statements when then condition of the if **statement is False**.As with if statements, the code inside the block should be indented.> x = 4> if x == 5:>> print("Yes")>else:>> print("No")Notice the colon after the else keyword.<jupyter_code>if 1 + 1 == 2:
  if 2*2 == 8:
    print("if")
  else:
    print("else")<jupyter_output>else<jupyter_text>**else Statements**Every if condition block can have only one else statement.In order to make multiple checks, you can chain if and else statement.For example, the following program checks and outputs the num variable's value as text:> num = 3> if num == 1:>> print("One")> else:>> if num == 2:>>> print("Two")>> else:>>> if num == 3:>>>> print("Three")>>> else:>>>> print("Something else")Indentation determines which **if/else** statements the code blocks belong to.<jupyter_code>num = 3
if num == 1:
  print("One")
else:
  if num == 2:
    print("Two")
  else:
    if num == 3:
      print("Three")
    else:
      print("Something else")
x = 10
y = 20

if x > y:
  print("if statement")
else:
  print("else statement")<jupyter_output>else statement<jupyter_text>**elif Statements**Multiple if/else statements make the code long and not very readable.The elif (short for else if) statement is a shortcut ot use when chaining if and else statements, making the code shorter. The same example from the previous part can be rewritten using elif statements:> num = 3> if num == 1:>> print("One")> elif num == 2:>> print("Two)> elif num == 3:>> print("Three")> else:>> print("Something else")As you can see in the example above, a series of if elif statements can have a **final** **else** block, which is called if **none of the if or elif expressions is True**.The **elif** statement is **equivalent** to an else/if statement. It is used to make the code shorter, more readable, and avoid indentation increase.<jupyter_code>num = 3
if num == 1:
  print("One")
elif num == 2:
  print("Two")
elif num == 3:
  print("Three")
else:
  print("Something else")<jupyter_output>Three<jupyter_text>**Boolean Logic****Boolean logic** is used to make more complicated conditions for if statements that rely on more than one condition.Python's Boolean operators are - and - or - notThe **and** operator takes two arguments, and evaluates and **True**, if, and only if, **both of its arguments are True**. Otherwise, it evaluates to False.> print(1 == 1 and 2 == 2)> True> print(1 == 1 and 2 == 3)> False> print(1 != 1 and 2 == 2) > False> print(2  6)> FalseBoolean operator can be used in expression as many times as needed.<jupyter_code>if (1==1) and (2+2>3):
  print("true")
else:
  print("false")<jupyter_output>true<jupyter_text>**Boolean Or**The **or** operator also takes two arguments. It evaluates to **True** if **either** **(or both)** of its arguemnts are **True**, and False if both arguments are False> print(1 == 1 or 2 == 2)> True> print(1 == 1 or 2 == 3)> True> print(1 != 1 or 2 == 2)> True> print(2  6)> FalseBesides values, you can also compare variables.<jupyter_code>age = 15 
money = 500

if age > 18 or money > 100:
  print("Welcome")<jupyter_output>Welcome<jupyter_text>**Boolean Not**Unlike other operators we've seen so far, **not** takes one argument, and **inverts it**.The result of - **not True** is **False** - **not False** goes to **True**> print(not 1 == 1)> False > print(not 1 > 7)> TrueYou can chain multiple conditional statements in an if statement using the Boolearn operators.<jupyter_code>if not True:
  print("1")
elif not (1+1 == 3):
  print("2")
else:
  print("3")<jupyter_output>2<jupyter_text>**Operator Precedence**Operator precedence is a very important concept in programming. It is an extension of the methematical idea of order of operations (multiplication being performed before addition, etc.) to include other operators, such as those in Boolean logic.The below code shows that == has a higher precedence than or> print(False == False or True)> True> print(False == (False or True))> False> print((False == False) or True)> True**Python's order** of operations is the same as that of normal mathematics: - parentheses first, - then exponentiation, - then multiplication / division, - and then addition / subtraction.<jupyter_code>if 1 + 1*3 == 6:
  print("Yes")
else:
  print("No")<jupyter_output>No<jupyter_text>**Chaining Multiple Conditions**You can chain multiple conditional statements in an if statement using The Boolean operators.For example, we can if the value of a grade is between 70 and 100:> grade = 88> if (grade >= 70 and grade <= 100):>>print("passed!")You can use multiple and, or, not operators to chain multiple conditions together.<jupyter_code>x = 4 
y = 2
if not 1 + 1 == y or x == 4 and 7 == 8: # not [(true) or [(true) and (false)]]
  print("Yes")
elif x > y:
  print("No")<jupyter_output>No<jupyter_text>**Lists****Lists** are used to store items.A list is created using square brackets with commas separating items.> words = ["Hello", "world", "!"]In the example above the words list contains three string items.A certain item in the list can be accessed by using its index in square brackets.For example:> words = ["Hello", "world", "!"]> print(words[0])> print(words[1])> print(words[2])The first list item's index is 0, rather than 1, as might be expected.<jupyter_code>nums = [5,4,3,2,1]

print(nums[1])<jupyter_output>4<jupyter_text>**Lists**Sometimes you need to create an empty list and populate it later during the program. For example, if you are creating a queue management program, the queue is going to be empty in the beginning and get populated with people data later.An empty list is created with an empty pair of square brackets.> empty_list = []> print(empty_list)In some code samples you might see a comma after the last item in the list. It's not mandatory, but perfectly valid.<jupyter_code>nums = [2,]

i = 0

for num in nums:
  i += 1
  print(i)<jupyter_output>1<jupyter_text>**Lists**Typically, a list will contain items of a single item type, but it is also possible to include several different types.Lists can also be nested within other lists.> number = 3> Things = ["string", 0, [1, 2, number], 4.56]> print(things[1])> print(things[2])> print(things[2][2])Nested lists can be used to represent 2D grids, such as matrices. For example:> m = [>     [ 1, 2, 3 ],>     [ 4, 5, 6 ],>     ] > print(m[1][2])A matrix-like structure can be used in cases where you need to store data in row-column format. For example, when creating a ticketing program, the seat numbers can be stored in a matrix, with their corresponding rows and numbers.The code above outputs the 3rd item of the 2nd row.<jupyter_code>list = [42, 55, 67]

print(list[2])<jupyter_output>67<jupyter_text>**Lists**Some types, such as strings, can be indexed like lists. Indexing strings behaves as though you are indexing a list containing each character in the string.For example:> str = "Hello world!"> print(str[6])Space(" ") is also a symbol and has an index.Trying to access a non-existing index will produce an error.<jupyter_code>num = [5,4,2,[2],1]

print(num[0])
print(num[3][0])
#print(num[5]) error<jupyter_output>5
2<jupyter_text>**List Operations**The item at a certian index in a list can be reassigned For example:> nums = [7, 7, 7, 7, 7]> nums[2] = 5> print(nums)You can replace the item with an item of a different type.<jupyter_code>nums = [1, 2, 3, 4, 5]
nums[3] = nums[1]

print(nums[3])<jupyter_output>2<jupyter_text>**List Operations**Lists can be added an multiplied in the same way as strings. For example:> nums = [1, 2, 3]> print(nums + [4, 5, 6])> print(nums *3)Lists and strings are similar in many ways string can be thought of as lists of characters that can't be changedFor example, the string "Hello" can be thought of as a list, where each character is an item in the list. The first item is "H", the second item is "e", and so on.<jupyter_code>nums = [33, 42, 56]
nums[1] = 22

print(nums)<jupyter_output>[33, 22, 56]<jupyter_text>**List Operations**To check if an item is in a list, the **in** operator can be used. - It returns **True** if the item occurs one or more times in the list, and- **False** if it doesn't> words = ["spam", "egg", "spam", "sausage"]> print("spam" in words)> print("egg" in words)> print("tomato" in words)The **in** operator is also used to determien whether or not a string is a substring of another string.<jupyter_code>nums = [10, 9, 8, 7, 6, 5]
nums[0] = nums[1] - 5

if 4 in nums:
  print(nums[3])
else:
  print(nums[4])<jupyter_output>7<jupyter_text>**List Operations**To check if an item is not in a list, you can use the **not** operator in one of the following ways:> nums = [1, 2, 3]> print(not 4 in nums)> print(4 not in nums)> print(not 3 in nums)> print(3 not in nums)> True > True> False> False<jupyter_code>letters = ['a', 'b', 'z']

if "z" in letters:
  print("Yes")<jupyter_output>Yes<jupyter_text>**List Functions**The **append** method adds an item to the end of an existing list.For example:> nums = [1, 2, 3]> nums.append(4)> print(nums)> [1, 2, 3, 4]The **dot** before append is there because it is a **method** of the list class.<jupyter_code>words = ["hello"]
words.append("world")

print(words[1])<jupyter_output>world<jupyter_text>**List Functions**To get the number of items in a list, you can use the **len** function.> nums = [1, 3, 5, 2, 4]> print(len(nums))Unlike the index of the items, **len** does not start with 0. So, the list above contains 5 items, meaning **len** will return 5.**len** is written before the list it is being called on, without a dot.<jupyter_code>letters = ["a", "b", "c"]
letters.append("d")

print(len(letters))<jupyter_output>4<jupyter_text>**List Functions**The **insert** method is similar to **append**, except that it allows you to insert a new item at any position in the list, as opposed to just at the end.> words = ["Python", "fun]> index = 1> words.insert(index, "is")> print(words)> Python is funElements, that are after the inserted item, are shifted to the right.<jupyter_code>nums = [9, 8, 7, 6, 5]
nums.append(4)
nums.insert(2,11)

print(nums)
print(len(nums))<jupyter_output>[9, 8, 11, 7, 6, 5, 4]
7<jupyter_text>**List Functions**The **index** method finds the first occurrence of a list item and returns its index.if the item isn't in the list, it raises a ValueError.> letters = ['p', 'q', 'r', 's', 'p', 'u']> print(letters.index('r))> print(letters.index('p'))> print(letters.index('z'))> 2> 0> ValueError.there are a few more useful functions and methods for lists.- max(list): Returns the list item with the maximum value.- min(list): Returns the list item with the minimum value.- list.count(item): Returns a count of how many times an item occurs in a list.- list.remove(item): Removes an object from a list.- list.reverse(): Reverses items in a list.For example, you can count how many 42s are there in the lsit using: **items.count(42)**where items is the name of our list.<jupyter_code>list = ['x', 'y']

list.append('z')

print(len(list))<jupyter_output>3<jupyter_text>**while Loops**A **while** loop is used to repeat a block of code multiple times.For example, let's say we need to process multiple user inputs, so that each time the user inputs something, the same block of code needs to execute.Below is a **while** loop containing a variable that counts up for 1 to 5, at which point the loop terminates.> i = 1> while i <= 5:>> print(i)>> i = i + 1>print("Finished!")During each loop iteration, the i variable will get incremented by one, until it reaches 5.So, the loop will execure the print statement 5 times.The code in the body of a **while** loop is executed repeatedly, This is called **iteration.**<jupyter_code>i = 3

while i >= 0:
  print(i)
  i = i - 1<jupyter_output>3
2
1
0<jupyter_text>**while Loops**You can use multiple statements in the while loop. For example, you can use an **if** statement to make decisions. This can be useful, if you are making a game and need to loop through a number of player actions and add or remove points of the player.The code below uses an if/else statement inside a while loop to separate the even and odd numbers in the range of 1 to 10:> x = 1> while x < 10:>> if x % 2 == 0:>>> print(str(x) + " is even")>> else:>>> print(str(x) + " is odd")>> x += 1**str(x)** is used to convert the number x to a string, so that it can be used for concatenationIn console, you can stop the program's execution by using the Ctrl-c shortcut or by closing the program.<jupyter_code>x = 0

while x <= 20:
  print(x)

  x += 2<jupyter_output>0
2
4
6
8
10
12
14
16
18
20<jupyter_text>**break** To end a while loop prematurely, the break statement can be used. For example, we can break an infinite loop if some condition is met:> i = 0> while True:>> print(i)>> i = i + 1>> if i >= 5:>>> print("Breaking")>>> break>print("Finished")**while True** is a short and easy way to make an infinite loop.**An example use case of break:**An infinite while loop can be used to continuously take user input. For example, you are making a calculator and need to take numbers from the user to add an stop, when the user enters **"stop"**.In the case, the **break** statement can be used to end the infinite loop when the user input equals "stop".Using the **break** statement outside of a loop causes an error.<jupyter_code>i = 5 
while True:
  print(i)
  i = i -1
  if i <= 2:
    break<jupyter_output>5
4
3<jupyter_text>**continue**Another statement that can be used withing loops is **continue**  Unlike **break**, **continue** jumps back to the top of the loop, rather than stopping it.Basically, the **continue** statement stops the current iteration and continues with the next one.**For example:**> i = 1 >while i <= 5:>> print(i)>> i += 1>> if i == 3:>> print("Skipping 3")>> continue**An example use case of continue:**An airline ticketing system needs to calculate the total cost for all tickets purchased. The tickets for childeren under the age of 1 are free. We can use a while loop to iterate through the list of passengers and calculate the total cost of their tickets. Here, the **continue** statement can be used to skip the children.Using the **continue** statement outside of a loop causes and error. **for Loop**The for loop is used to iterate over a given sequence, such as lists or strings. The code below outputs each item in the list and adds an exclamation mark at the end:> words = ["hello", "world", "spam", "eggs"]> for word in words:>> print(word + "!")In the code above, the word variable represents the corresponding item of the list in each iteration of the loop. During the 1st iteration, word is equal to "hello", and during the 2nd iteration it's equal to "world", and so on.<jupyter_code>letters = ['a', 'b', 'c']

for l in letters:
  print(l)<jupyter_output>a
b
c<jupyter_text>**for Loops**The **for** loop can be used to iterate over strings.**For example:**> str = "testing for loops"> count = 0> for x in str:>> if(x == 't'):>>> count += 1> print(count)The code above defines a count variable, iterates over the string and calculates the count of 't' letters in it. During each iteration, the **x** variable represents the current letter of the string.The count variable is incremented each itme the letter 't' is found, thus, at the end of the loop it represents the number of 't' letters in the string.Similar to **while** loops, the **break** and **continue** statemetns can be used in **for** loops, to stop the loop or jump to the next iteration.<jupyter_code>list = [2, 3, 4, 5, 6, 7]

for x in list:
 if (x % 2 == 1 and x > 4):
   print(x)
   break<jupyter_output>5<jupyter_text>** for vs while **Both, **for** and **while** loops can be used to execute a block of code for multiple times.It is common to use the **for** loop when the number of iterations is fixed. For example, iterating over a fixed list of items in a shopping list.The **while** loop is used in cases when the number of iterations is not known and depends on some calculations and conditions in the code block of the loop.For example, ending the loop when the user enters a specific input in a calculator program.Both, **for** and **while** loops can be used to achieve the same results, however, the **for** loop has cleaner and shorter syntax, makign it a better choice in most cases. **Range**The **range()** function returns a sequence of numbers.By default, it starts from 0, increments by 1 and stops **before the specified number.**The code below generates a list containing all of the integers, up to 10.> numbers = list(range(10))> print(numbers)In order to output the range as a list, we need to explicitly convert it to a list, using the **list()** function.<jupyter_code>list = range(5)

print(list[4])<jupyter_output>4<jupyter_text>**Range**if range is **called with one argument**, it produces an object with values **from 0 to that argument**.if it is **called with two arguemnts**, it produces values **from the first to the second**.For example:> numbers = list(range(3, 8))> print(numbers)> print(range(20) == range(0, 20))Remember, the second argument is not included in the range, so range(3, 8) will not include the number 8.<jupyter_code>print(len(range(20)) == len(range(0,20)))
nums = range(5, 8)

print(len(nums))<jupyter_output>3<jupyter_text>**Range****range** can have a **third argument**, which determines the **interval** of the sequence produced, also called the **step**> numbers = list(range(5, 20, 2))> print(numbers)We can also create list of **decreasign numbers**, using a **negative number** as the third argument, for example **lsit(range(20, 5, -2)).**<jupyter_code>nums = range(3, 15, 3)
print(nums[2])<jupyter_output>9<jupyter_text>**for Loops**The **for** loop is commonly used to repeat some code a certain number of times. This is done by combining for loop with **range** objects.> for i in range(5):>> print("hello!")You don't need to call **list** on the **range** object when it is used in a **for** loop, because it isn't being indexed, so a list isn't required<jupyter_code>for i in range(0, 20, 2):
  print(i)
list = [1, 1, 2, 3, 5, 8, 13]

print(list[list[4]])
for i in range (10):
  if not i % 2 == 0 :
    print(i + 1)
while False:
  print("Looping...")
list = [1, 2, 3, 4]
if len(list) % 2 == 0:
  print(list[0])
letters = ['x', 'y', 'z']
letters.insert(1, 'w')
print(letters[2])
list = [1, 2, 3]
for var in list:
  print(var)<jupyter_output>1
2
3
{'fraction_non_alphanumeric': 0.12477991964245407, 'fraction_numerical': 0.021488871834228703, 'mean_word_length': 4.883665338645418, 'pattern_counts': {'":': 0, '<': 116, '<?xml version=': 0, '>': 366, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '34', 'dir': 'jupyter-structured-clean-dedup', 'id': '477225', 'license': 'Apache-2.0', 'n_tokens_mistral': 7100, 'n_tokens_neox': 6618, 'n_words': 3153, 'path': 'Py3_Control_Structures.ipynb', 'repo_name': 'niklaust/self_study', 'size': '22151'}