<jupyter_start><jupyter_text>LoopsLoops in programming give us an option to repeat a section of code, either for a fixed number of times, or more generally until a certain condition is met. Python gives us two keywords for creating loops in our code, and they are `for` and `while`. In most situations you can use these interchangeably, however the usual convention is to use a for loop when you know how many times you want to repeat the execution of your code block (i.e. you repeat things a fixed number of times) and use a while loop when repeating a code block for an unknown number of times, until a condition is met.  For LoopsWe'll start by looking at for loops. In the example below, we take a list of integers, and we want to print them all out. We could do this just using the indexing operator that we have seen previously, but that requires us knowing the size of the list in advance to make sure we have enough print statements to print out every element. However, using a for loop it won't matter how many elements are in our list, Python will handle things for us.<jupyter_code>L = [5,8,2,6,1,7,3,4]

for e in L:
    print(e)<jupyter_output><empty_output><jupyter_text>In this notation the variable `e` only exists inside this loop, and it will hold one element from the list `L` at a time. The keyword `in` makes the statement read sensibly, and gives you some intuition in to what Python is trying to do. The object to the right of `in` in our code is a list, and a list is an example of an iterable object in Python. We're not going to go into any details on Iterators and iterable objects - but we should be aware that of the data types and data structures we have already seen, the following are iterable objects:1. Lists2. Tuples3. Dictionaries4. Sets5. StringsWhen using a for loop, we should always have the general structure:`for  in :     ` The sequence always has to be an iterable object, so our code example worked because a list is an iterable data structure. The general idea is that the code looks at each element of the list in turn, and temporarily stores that elements in the variable `e`. We can use that variable in the indented code within our loop, and then once we have finished all of the indented code, we return to the for loop, grab the next element of our list, place that in the variable `e` and continue. Once we've used all of the elements of the list, we move on to the non-indented code that follows the for loop. If I wanted to do something slightly more interesting, I could take all of the elements of the list, sum them up, and print the sum as in the example below:<jupyter_code>L = [5,8,2,6,1,7,3,4]
sum = 0

for e in L:
    sum = sum + e
    
print(sum)<jupyter_output><empty_output><jupyter_text>We take each element of the list in turn, store it in the variable `e` and then add it to `sum`. Once we have used all of the values in the list, the loop will end, and the print statement will be executed, telling us the sum of all of the numbers in our input list.You will notice above we said that a string is an iterable object. It is iterable in the sense that each character can be treated as an individual element, so the code we used originally on a list, we can also use on a string.<jupyter_code>S = "Hello World"

for s in S:
    print(s)<jupyter_output><empty_output><jupyter_text>For loops don't really get any more complicated than that! However, there is one more iterable object you should be aware of, as it is particularly useful, and that is the range object. We can use the `range` function to generate a range object, which we can consider as a sequence of values that are iterable. Below we create a range object using the `range` function. If we print out the variable `x`, and the type of `x` we see the information returned isn't all that useful.<jupyter_code>x = range(0,10)
print(x)
type(x)<jupyter_output><empty_output><jupyter_text>If we use `x` in a for loop however, it suddenly starts to do useful things for us!<jupyter_code>for i in x:
    print(i)<jupyter_output><empty_output><jupyter_text>Now we can see that `x` is an object pretty similar to a list, and for our purposes we can consider it as a list. In this case the range function has given us a sequence of values from 0 to 9 to use in any way we wish. A common use of the range function is to access elements of a list via their index, with the indexes produced by range. See the example below:<jupyter_code>L = [5,8,2,6,1,7,3,4]
sum = 0

for i in range(0,len(L)):
    sum = sum + L[i]
    
print(sum)<jupyter_output><empty_output><jupyter_text>This example is almost identical to one we saw earlier, except now we access the elements of the list via their index, using the indexing operator. The range function is creating a sequence of values from 0 to 7, the variable `i` takes each of those values in turn, and we use `i` to obtain the data in that position in the list via `L[i]`.We can use the range function in a similar way to how slicing worked with lists. The notation is:`range(start, stop, step)`If we don't provide range with a start value Python will assume the value should be 0, and if we don't provide a step value Python will default to a step of 1. Take a look at the examples below and make sure you understand the sequences of numbers being produced.<jupyter_code>x = range(1,20,2)
for i in x:
    print(i)
x = range(20,0,-3)
for i in x:
    print(i)
x = range(-10,10)
for i in x:
    print(i)<jupyter_output><empty_output><jupyter_text>The important thing to notice is that the sequence generated by `range` will stop before the stop value you provide. This is a common mistake to make, so while you get used to using the range function is might be useful to you to print out the sequence generated by range before you start using those values in the rest of your code.By combining the range function with the for loops that we've seen before, you can now solve the vast majority of programming problems that you might encounter! However, we don't always have the convenience of knowing how many times a loop needs to be run as we did in all of the examples above, or we may be too lazy to figure it out. In either case, we could use a while loop instead of a for loop.   While LoopsWe can start by looking at the same example we saw previously - summing up all of the numbers in a list. As we mentioned previously, we can often use for and while loops interchangeably, and below is an example of how we might adapt our code to work with a while loop instead.<jupyter_code>L = [5,8,2,6,1,7,3,4]
sum = 0
i=0

while (i<len(L)):
    sum = sum + L[i]
    i = i+1
    
print(sum)<jupyter_output><empty_output><jupyter_text>We have replaced the for loop, with a while loop and a condition. The while loop will only continue to execute while the condition remains true. Once the condition becomes false, the loop will stop executing, and we move to the next piece of non-indented code. The general structure of a while loop looks like this:`while :     `If we want to visualise how the while loop works we can draw a diagram similar to those we saw for if statements: The example above is important to demonstrate that quite often problems can be solved easily with either a for loop or a while loop, depending on which you are more comfortable to use. The example below is another problem that could be solved with either a for loop or a while loop, but in this case it is much more natural to use a while loop. We could work out how many time this loop would execute, and use a for loop that executes that fixed number of times, but that would require a lot of extra work on our part!<jupyter_code>i=1

while (i<1000000):
    i = i*2
    print(i)<jupyter_output><empty_output><jupyter_text>Now we can introduce our final Python keyword for this topic, the keyword `break`. We can use `break` to escape from a loop, either a for loop or a while loop, and move on to the next section of non-indented code. For example, we can move the condition on our while loop inside the loop instead, and use an if statement and the `break` keyword to do the same job. Let's tackle the same problem as above, but this time using `break`.<jupyter_code>i=1

while True:
    i = i*2
    print(i)
    if (i>=1000000):
        break<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.051649410065013245, 'fraction_numerical': 0.009872381411028173, 'mean_word_length': 4.195121951219512, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 42, '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': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '112505', 'license': 'Apache-2.0', 'n_tokens_mistral': 2269, 'n_tokens_neox': 2212, 'n_words': 1412, 'path': 'Topic 5 - Control Flow/3.2 - Loops.ipynb', 'repo_name': 'KaiyeYang-git/Introduction-to-Computer-Science', 'size': '8306'}