Chapter 3: Custom functions

Euclidean distance function

face Josiah Wang

Now, let’s try to implement the function, step-by-step. Remember, we will incrementally test as we code to avoid trying to debug our code later!

Let’s implement the first step of the function. What should you first do? Well you should probably first check the definition of Euclidean distance:

D((x_1, y_1), (x_2, y_2)) = \sqrt{\left(x_1-x_2\right)^2 + \left(y_1-y_2\right)^2}

Well, looking at the equation, the smallest thing you can do first is to compute the difference between x_1 and x_2, and between y_1 and y_2. So let’s do that.

def euclidean_distance(x1, y1, x2, y2):
    """ your docstring here ...
    """
    dx = x2 - x1
    dy = y2 - y1
    print(f"dx: {dx}, dy: {dy}")
    return 0.0

# your test code
distance = euclidean_distance(2, 3, -2, 1)
expected_answer = 4.4721
epsilon = 0.000001
if abs(distance - expected_answer) < epsilon:
    print("Test passed")
else:
    print(f"Test failed. {expected_answer} expected, but {distance} returned.")

Running your code, you should see:

dx: -4, dy: -2
Test failed. 4.4721 expected, but 0.0 returned.

Calculating dx and dy manually, we can confirm that these are indeed correct (-2-2==-4, 1-3==-2).

Now, continuing our implementation, we can now square both dx and dy and sum them up.

def euclidean_distance(x1, y1, x2, y2):
    """ your docstring here ...
    """
    dx = x2 - x1
    dy = y2 - y1
    d_squared = dx**2 + dy**2
    print(f"d_squared: {d_squared}")
    return 0.0

# your test code
...

Running your code again, you should get d_squared: 20, which you can manually confirm is correct.

Finally, you can compute the square root. Remember to import math to use the sqrt() function. And since that’s the Euclidean distance, we just return the value.

import math

def euclidean_distance(x1, y1, x2, y2):
    """ your docstring here ...
    """
    dx = x2 - x1
    dy = y2 - y1
    d_squared = dx**2 + dy**2
    dist = math.sqrt(d_squared)
    return dist

# your test code
...

Running our test code, looks like we are technically correct. Just need to increase the value of epsilon (if that is desirable).

Test failed. 4.4721 expected, but 4.47213595499958 returned.

After decreasing epsilon to 0.0001, our test passed.

Test passed

Now that your function is working correctly, you can now choose to rewrite your code to make it more compact, as long as it does not make your code harder to read. Here, I chose to skip the auxiliary variable d_squared and instead used the whole expression directly as an argument in math.sqrt().

import math

def euclidean_distance(x1, y1, x2, y2):
    """ your docstring here ...
    """
    dx = x2 - x1
    dy = y2 - y1
    return math.sqrt(dx**2 + dy**2)

# your test code
...

Remember to run your code again to make sure it still passes your test!

So, to recap, you have done two things here: you have implemented a function, and you have implemented it incrementally (test-as-you-code) and by using a test-first approach.