Применить функцию к одному элементу списка в Python - PullRequest
2 голосов
/ 02 апреля 2010

Я ищу краткий и функциональный способ применения функции к одному элементу кортежа и возврата нового кортежа в Python.

Например, для следующего ввода:

inp = ("hello", "my", "friend")

Я бы хотел получить следующий вывод:

out = ("hello", "MY", "friend")

Я предложил два решения, которые меня не устраивают.

Один использует функцию высшего порядка.

def apply_at(arr, func, i):
    return arr[0:i] + [func(arr[i])] + arr[i+1:]

apply_at(inp, lambda x: x.upper(), 1)

Используются списки (предполагается, что длина кортежа известна).

[(a,b.upper(),c) for a,b,c in [inp]][0]

Есть ли лучший способ? Спасибо!

Ответы [ 6 ]

6 голосов
/ 02 апреля 2010

Вот версия, которая работает на любой итерации и возвращает генератор:

>>> inp = ("hello", "my", "friend")
>>> def apply_nth(fn, n, iterable):
...    return (fn(x) if i==n else x for (i,x) in enumerate(iterable))
... 
>>> tuple(apply_nth(str.upper, 1, inp))
('hello', 'MY', 'friend')

Вы можете расширить это так, чтобы вместо одной позиции вы могли дать ей список позиций:

>>> def apply_at(fn, pos_lst, iterable):
...    pos_lst = set(pos_lst)
...    return (fn(x) if i in pos_lst else x for (i,x) in enumerate(iterable))
... 
>>> ''.join(apply_at(str.upper, [2,4,6,8], "abcdefghijklmno"))
'abCdEfGhIjklmno'
2 голосов
/ 02 апреля 2010

Может быть, кто-то вроде этого?

>>>inp = ("hello", "my", "friend")
>>>out =  tuple([i == 1 and x.upper() or x for (x,i) in zip(t,range(len(t)))])

>>> out
('hello', 'MY', 'friend')

Примечание: вместо (x,i) in zip(t, range(len(t))) я должен был подумать об использовании функции перечисления: (i,x) in enumerate(t)

Делая это немного более общим:
Вместо жесткого кодирования 1 мы можем поместить его в переменную.
Кроме того, используя для этой цели кортеж, мы можем применить функцию к элементам с несколькими индексами.

>>>inp = ("hello", "my", "friend")
>>>ix  = (0,2)
>>>out =  tuple([i in ix and x.upper() or x for (i, x) in enumerate(t)])

>>> out
('HELLO', 'my', 'FRIEND')

Кроме того, мы можем «заменить» zip () / enumerate () на map (), что-то вроде

out = tuple(map(lambda x,i : i == 1 and x.upper() or x, inp, range(len(inp)) ) )

Редактировать: (адрес комментария об указании функции для применения):
Может быть что-то так просто, как:

>>> f = str.upper  # or whatever function taking a single argument
>>> out = tuple(map(lambda x,i : i == 1 and f(x) or x, inp, range(len(inp)) ) )

Поскольку мы говорим о применении любой функции, мы должны упомянуть небольшое предостережение с конструкцией condition and if_true or if_false, которая не является точно заменой троичного оператора if / else найдено на других языках. Ограничение состоит в том, что функция не может возвращать значение, эквивалентное False (например, None, 0, 0.0, ''). Чтобы избежать этой проблемы, в Python 2.5 и более поздних версиях рекомендуется использовать истинный троичный оператор if-else, как показано в ответе Дейва Кирби (обратите внимание на синтаксис этого оператора when_true if condition else when_false)

2 голосов
/ 02 апреля 2010
>>> inp = "hello", "my", "friend"
>>> index = 1
>>> inp[:index] + ( str.upper(inp[index]),) + inp[index + 1:]
('hello', 'MY', 'friend')

Кажется простым, единственное, что вам, возможно, нужно знать, - это сделать один элемент кортежем: do (elt,)

1 голос
/ 02 апреля 2010

Я прокомментировал поддержку вашего первого фрагмента, но вот пара других способов записи:

(lambda (a,b,c): [a,b.upper(),c])(inp)

(не будет работать в Python 3.x.) И:

[inp[0], inp[1].upper(), inp[1]]
0 голосов
/ 02 апреля 2010

Мне также нравится ответ, который дал Дейв Кирби. однако, как объявление публичной службы, я хотел бы сказать, что это не типичный вариант использования кортежей - это структуры данных, которые возникли в Python как средство для перемещения данных (параметров, аргументов) в функции и из функций. .. они не были предназначены для программиста для использования в качестве общих массивоподобных структур данных в приложениях - вот почему существуют списки. естественно, если вам нужна постоянная функция неизменяемых кортежей только для чтения, это справедливый аргумент, но, учитывая вопрос OP, это должно было быть сделано вместо списков - обратите внимание, что существует дополнительный код для извлечения Разобрать и объединить получившийся результат и / или необходимость временного преобразования в список и обратно.

0 голосов
/ 02 апреля 2010

Я не понимаю, хотите ли вы применить определенную функцию к каждому элементу в кортеже, который проходит некоторый тест, или если вы хотите, чтобы она применялась к любому элементу, присутствующему в определенном индексе кортежа. Итак, я кодировал оба алгоритма:

Это алгоритм (написанный на Python), который я бы использовал для решения этой проблемы на функциональном языке, таком как схема:

Эта функция идентифицирует элемент, идентифицируемый с помощью id, применяет к нему func и возвращает список с этим элементом, измененным на выход func. Это будет сделано для каждого элемента, идентифицируемого как id:

def doSomethingTo(tup, id):
    return tuple(doSomethingToHelper(list(tup), id))

def doSomethingToHelper(L, id):
    if len(L) == 0:
        return L
    elif L[0] == id:
        return [func(L[0])] + doSomethingToHelper(L[1:], id)
    else:
        return [L[0]] + doSomethingToHelper(L[1:], id)


Этот алгоритм найдет элемент по индексу кортежа и применит к нему func, а затем вернет его в исходный индекс кортежа

.
def doSomethingAt(tup, i): 
    return tuple(doSomethingAtHelper(list(tup), i, 0))

def doSomethingAtHelper(L, index, i):
if len(L) == 0: 
        return L
elif i == index: 
        return [func(L[0])] + L[1:]
else: 
        return [L[0]] + doSomethingAtHelper(L[1:], index, i+1)
...