Python: уменьшить количество кортежей - PullRequest
4 голосов
/ 17 ноября 2011

Я пытаюсь вычислить в Python длину пути от точки A до точки B, проходящего через список промежуточных точек. Я знаю, как это сделать, но я хочу использовать встроенную функцию уменьшения .

Почему я пытался до сих пор, обратите внимание, что это совершенно неправильно , это:

reduce(lambda x,y: math.sqrt((y[1]-y[0])**2+(x[1]-x[0])**2) , ((1,2),(3,4),(1,8)))

Есть идеи?

Спасибо.

Ответы [ 8 ]

6 голосов
/ 17 ноября 2011

Вы должны составить карту, прежде чем уменьшать.

points = [(1, 2), (3, 4), (1, 8)]
distances = (math.hypot(b[0]-a[0], b[1]-a[1])
             for a, b in zip(points, points[1:]))
total_distance = sum(distances)

или, если вы должны использовать reduce(), хотя sum() лучше для этой цели:

import operator

total_distance = reduce(operator.add, distances)

Если у вас много очков, NumPy может помочь вам сделать это сразу, быстро:

import numpy

total_distance = numpy.hypot(*numpy.diff(numpy.array(points), axis=0)).sum()

Редактировать : используйте math.hypot() и добавьте NumPyспособ.

4 голосов
/ 17 ноября 2011

Это не красиво, но это можно сделать: -)

>>> tot = ((1,2),(3,4),(1,8))
>>> reduce(lambda d,((x0,y0),(x1,y1)): d + ((x1-x0)**2+(y1-y0)**2)**0.5, zip(tot[1:], tot[0:]), 0.0)
7.3005630797457695
2 голосов
/ 17 ноября 2011

reduce() просто неправильный инструмент для этой цели. возможно сделать с reduce(), но это немного странно:

def distance((x, d), y):
    return y, d + math.hypot(y[0] - x[0], y[1] - x[1])

print reduce(distance, [(3,4),(1,8)], ((1, 2), 0.0))[1]

печать

7.30056307975

Последний параметр, переданный в вызов reduce(), является начальной точкой и начальным значением расстояния.

1 голос
/ 17 ноября 2011

Это просто не тот код, который вы хотите написать.Сокращение не будет хорошим решением.

Я предлагаю итеративное.Это будет наиболее читаемое, питонное и поддерживаемое решение.

import math
path = [(1,2),(3,4),(1,8)]

def calc_dist(waypoints):
    dist = 0.0
    for i in range(len(waypoints) - 1):
        a = waypoints[i]
        b = waypoints[i+1]
        dist += math.hypot(a[0]-b[0], b[1]-a[1])
    return dist

print calc_dist( path )
1 голос
/ 17 ноября 2011

Вот мета-итератор redux, который можно комбинировать со встроенным reduce, чтобы получить желаемый результат.В этой реализации исключена всякая буферизация входной последовательности.

def redux(f):
  def execute(iterable):
    iterable = iter(iterable)
    try:
      state = iterable.next()
    except StopIteration:
      raise ValueError, 'empty sequences not supported'
    while True:
      newstate = iterable.next()
      yield f(state, newstate)
      state = newstate
  return execute

f = redux(lambda x, y: math.sqrt((y[0] - x[0])**2 + (y[1] - x[1])**2))
print reduce(operator.add, f(((1,2),(3,4),(1,8))))

Приведенные выше распечатки 7.30056307975.

Функция redux может быть обобщена для поддержки более двух аргументов одновременно.скользящее окно, используя inspect.getargspec для подсчета количества аргументов, требуемых его аргументом функции.

1 голос
/ 17 ноября 2011

уменьшение не работает таким образом, вы начинаете с начального значения а, которое вы указываете или берется в качестве первого элемента из вашей итерации.после этого вы передаете a, next_element в предоставленную функцию (лямбда) и сохраняете результат в a, повторяясь до тех пор, пока все элементы не будут повторены.

Вы можете делать то, что хотите, с помощью sum и map, сначала вычисляя все расстояния отодну точку к следующей и затем суммируя их:

path = [(1,2),(3,4),(1,8)]
sum(map(lambda x,y: math.sqrt((x[0]-y[0])**2+(x[1]-y[1])**2), path[:-1],path[1:]))

edit: или с помощью функции hypot (thx @ralu):

sum(map(lambda x,y: math.hypot(x[0]-y[0],x[1]-y[1]), path[:-1],path[1:]))
0 голосов
/ 17 ноября 2011

Просто для удовольствия, вот альтернативное решение с подходом, немного отличающимся от подхода reduce(sum, map(hypot, zip(...))).

tot = ((1,2),(3,4),(1,8))
reduce(lambda (d,(x,y)),b: (d+math.hypot(x-b[0],y-b[1]), b), tot, (0, tot[0]))[0]

Обратите внимание, что reduce фактически возвращает кортеж (расстояние, последняя точка),отсюда [0] в конце.Я думаю это было бы более эффективным, чем zip решения, но на самом деле не проверял.

0 голосов
/ 17 ноября 2011

Я знаю, что то, что я собираюсь предложить, не является идеальным, но я думаю, что это настолько близко, насколько я могу получить за мой вклад. Решить эту забавную проблему, даже если это не самое традиционное применение метода Reduce.

Ключевой проблемой, как представляется, является отслеживание расстояния от точки до точки без перезаписи самих точек - добавление еще одного «измерения» к каждой точке дает вам поле, с помощью которого вы можете отслеживать расстояние пробега.

iterable = ((1,2,0), (3,4,0), (1,8,0))
# originally ((1,2), (3,4), (1,8))

from math import sqrt

def func(tup1, tup2):
    '''function to pass to reduce'''

    # extract coordinates
    x0 = tup1[0]
    x1 = tup2[0]
    y0 = tup1[1]
    y1 = tup2[1]

    dist = tup1[2] # retrieve running total for distance

    dx = x1 - x0   # find change in x
    dy = y1 - y0   # find change in y

    # add new distance to running total
    dist += sqrt(dx**2 + dy**2) 

    # return 2nd point with the updated distance
    return tup2[:-1] + (dist,)  # e.g. (3, 4, 2.828)

Теперь уменьшите:

reduce(func, iterable)[-1]
# returns 7.3005630797457695

Таким образом, промежуточный кортеж кортежей (т.е. после одного «сокращения») становится:

((3, 4, 2.8284271247461903), (1,8,0))
...