# Loops
:::{note}
There's a saying: "In C, loops are your friend. In Python, loops are your enemy."
C++ is a compiled language. In the process of turning C++ code into machine language,
the compiler can do many things to automatically optimize your code; for example,
it can identify small loops (like the one in our example code fragment) and
arrange for it to be executed in a CPU's memory cache.
Python can't do this. It has to interpret each line one-by-one. In a loop, it
goes through each line, then "backtracks" to the beginning of the loop and
freshly interprets each line again.
There are some tricks to get around this. Mostly they involve using Python to call
routines written in C.[^bias]
:::
As a first step, let's get rid of one "cheat" that I put into our code
fragements. The `while` statement has its uses, but they're associated
with potentially varying logical conditions like reading a file. If
you're incrementing a number by a constant interval, then both Python
and C++ have a better way to write a loop.[^lower]
:::{code-block} python
:name: python-loop
:caption: A `for` loop in Python
# For each user, add a displacement to the distance array
interval = 10
scale = interval + 3
limit = interval + 1
# Assuming we want j=0:
for j in range(0,limit):
distance[j] += scale*j
:::
:::{code-block} c++
:name: cpp-loop
:caption: A `for` loop in C++
// For each user, add a displacement to the distance array
int interval = 10;
int scale = interval + 3;
int limit = interval + 1;
// Assuming that we want j=0:
for ( int j=0; j < limit; ++j ) {
distance[j] += scale*j
}
:::
There's not much more we can do for the C++ code,[^vec] so we'll
focus on the Python version of the rest of this section.
I didn't supply a defintion for `distance`. Given the use of
`distance[j]`, it could be simple Python
[array](https://www.w3schools.com/python/python_arrays.asp). But if
you've used Python before, you're probably screaming at me: "Use
[numpy](https://numpy.org/doc/stable/user/basics.html)!"
The length of the `distance` array isn't specified in the code
fragment. In theory, it could be larger than `limit`. Let's be general
for the moment, and assume the length of `distance` is greater than
`limit`.
:::{code-block} python
import numpy as np
distances = 20 # or some other value
# Create a numpy array of length 'distances'
distance = np.zeros((distances))
:::
Given the artificial nature of the original code fragment, the fastest
way to perform this task is to use `numpy`'s array features:
:::{code-block} python
:name: python-numpy
:caption: The Python code using numpy
# For each user, add a displacement to the distance array
interval = 10
scale = interval + 3
limit = interval + 1
j = 0 # or previously calculated elsewhere
distance[j:limit] += scale * np.arange(j,limit)
:::
This will give us C-level speed. It's up to you to decide whether this
gives us Python-level clarity.[^error]
Here's a [more detailed
tutorial](https://www.pythonlikeyoumeanit.com/Module3_IntroducingNumpy/VectorizedOperations.html)
on using numpy to perform faster computations.
[^bias]: At this point in the tutorial, you already know that I'm
biased in favor of C++ over Python. So I can't help but be snide
and point out that if you're using Python to call C, write not
write your code in C in the first place?
The usual reason to prefer Python is its development cycle: You can
quickly test small fragments and integrate them into your larger
program. But now you know ROOT, which has a C++ interpreter that
lets you do the same thing.
With that foolishness off my chest, I will continue to support
and inform your use of Python. I've got at least one more lifetime
I can spend learning more about the language!
[^lower]: I'm now using a lower limit of `j=0` in the loop, which I
did not explictly do when I first introduced the fragments. In the
"real" world, if the value of `j` was more complicated, it would
not change the loop code by much... except that I would not have
continued to use the simple letter `j` for the name of the
variable.
[^vec]: Though as we'll see in the next section, there's a way to
improve the C++ *compilation*, as opposed to the code.
[^error]: Although we've achieved faster code, we haven't necessarily
achieved more robust code. What if the value of `limit` is greater
than the length of `distance`?
:::{figure-md} good_code-fig
:align: center
by Randall Munroe
:::