0

I want to rewrite this code, so it won't use the range() function. I've had a search around, but it only suggests using enumerate() in its place. But I'd like for it to be done as a regular for loop, even if it's a more difficult or complex way, so I can understand how and why the range() function simplifies the code.

What I've written so far:

def distance(x1, x2) :
    return math.sqrt( ((x2[0]-x1[0])**2) + ((x2[1]-x1[1])**2) )


def route(points):
    total = 0
    for element in range(1, len(points)):
        total += distance(points[element - 1], points[element])
    return total


route([(4, 2), (6, 3), (5, 1)])

I'm unsure whether the distance function may also need to be altered to work through the list of tuples. But I essentially want to have the total distance between one point and the next as indexed in the list, as if something were travelling between the points.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
elizac
  • 11
  • 3
  • Please take a quick look at https://towardsdatascience.com/python-basics-iteration-and-looping-6ca63b30835c for various looping examples – donPablo Apr 18 '22 at 22:46
  • Adding onto @donPablo https://towardsdatascience.com/exploring-python-range-function-d509ebd36ec#:~:text=The%20range()%20is%20an,stops%20before%20the%20given%20number. for seeing what the python range function does. – JadenJin Apr 18 '22 at 22:53

3 Answers3

0

You can increment your own index variable to access the previous element

def route(points):
    index = 0
    total = 0
    for point in points[1:]:
        total += distance(points[index], point)
        index += 1
    return total
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • @elizac: To be clear, there's no reason to do it this way; using `enumerate` would be strictly superior (and solutions using `zip` or `pairwise` better than that). There's never a situation in which writing your own code equivalent to `enumerate` is an improvement over just using `enumerate`, or an even more Pythonic solution that avoids using indices entirely. – ShadowRanger Apr 18 '22 at 23:03
  • @ShadowRanger I think he's just trying to understand what `enumerate()` is doing, by implementing the code explicitly. – Barmar Apr 18 '22 at 23:04
0

Your data [(4, 2), (6, 3), (5, 1)] here is a list, and therefore iterable, so I suggest that you iterate over it directly rather than trying to index it.

The problem is, you want to compare successive elements of the data. Typically the way we'd iterate through two lists together would be via zip.

We can actually do that here, as follows:

def distance(x1, x2) :
    # if you're importing `math` anyway, might as well do this:
    return math.dist(x1, x2)

def route(points):
    total = 0
    for current, preceding in zip(points[1:], points):
        total += distance(preceding, current)
    return total

if __name__ == "__main__":
    points = [(4, 2), (6, 3), (5, 1)]
    print(route(points))

Note that the iteration will stop when points[1:] runs out of elements, so the last element of points will be ignored - no double-counting here.

Paddy Alton
  • 1,855
  • 1
  • 7
  • 11
0

You can use zip instead (I believe the following is quite pythonic):

import math

def distance(x1, x2):
    return math.sqrt((x2[0]-x1[0])**2 + (x2[1]-x1[1])**2)

def route(points):
    return sum(distance(p1, p2) for p1, p2 in zip(points, points[1:]))

print(route([(4, 2), (6, 3), (5, 1)])) # 4.47213595499958

An alternative is to use itertools.pairwise (for Python 3.10+):

return sum(distance(p1, p2) for p1, p2 in itertools.pairwise(points))
j1-lee
  • 13,764
  • 3
  • 14
  • 26
  • 1
    Heh, you beat me to the punch with essentially identical solutions (and moved it to a genexpr to allow the use of `sum` for more compact code). Up-voted. A note to the OP: Even if you're on pre-3.10 Python, you can just copy the "equivalent code" from the `pairwise` docs and add an import for `from itertools import tee` and you'll get equivalent code (if not *quite* as performant). And unlike all the solutions that use `zip` + a slice, which only work on sequences, `pairwise` (recipe or built-in) will work on arbitrary iterables, including itera*tors*. – ShadowRanger Apr 18 '22 at 22:58