如何使用 Python reduce 或 list comprehension 将函数列表按顺序应用于字符串?
How to apply a list of functions sequentially to a string using Python reduce or list comprehension?
问题陈述
我想将函数列表 fs = [ f, g, h ]
按顺序应用于字符串 text=' abCdEf '
类似于f( g( h( text) ) )
。
这可以使用以下代码轻松完成:
# initial text
text = ' abCDef '
# list of functions to apply sequentially
fs = [str.rstrip, str.lstrip, str.lower]
for f in fs:
text = f(text)
# expected result is 'abcdef' with spaces stripped, and all lowercase
print(text)
使用functools.reduce
似乎 functools.reduce
应该在这里完成这项工作,因为它在每次迭代时“消耗”函数列表。
from functools import reduce
# I know `reduce` requires two arguments, but I don't even know
# which one to chose as text of function from the list
reduce(f(text), fs)
# first interaction should call
y = str.rstrip(' abCDef ') --> ' abCDef'
# next iterations fails, because tries to call ' abCDef'() -- as a function
不幸的是,这段代码不起作用,因为每次迭代 returns 都是一个 字符串 而不是一个函数,并且失败并返回 TypeError
:'str' object is not callable
.
问题:是否有使用 map
、reduce
或 list comprehension
解决此问题的解决方案?
reduce
可以接受三个参数:
reduce(function, iterable, initializer)
这三个参数一般是什么?
function
是两个参数的函数。我们称这两个参数为 t
和 f
.
- 第一个参数
t
将从 initializer
开始;然后将继续作为先前调用 function
. 的 return 值
- 第二个参数
f
取自 iterable
。
我们案例中的这三个参数是什么?
- 可迭代对象是您的函数列表;
- 第二个参数
f
将成为函数之一;
- 第一个参数
t
必须是文本;
- 初始化器必须是初始文本;
function
的 return 必须是结果文本;
function(t, f)
必须是 f(t)
.
最后:
from functools import reduce
# initial text
text = ' abCDef '
# list of functions to apply sequentially
fs = [str.rstrip, str.lstrip, str.lower]
result = reduce(lambda t,f: f(t), fs, text)
print(repr(result))
# 'abcdef'
你可以试试这个:
import functools
text = ' abCDef '
fs = [str.rstrip, str.lstrip, str.lower]
text = functools.reduce(lambda store, func: func(store), fs, text)
print(text)
我认为您误解了 reduce 的工作原理。 Reduce 将可迭代对象减少为单个值。回调函数可以接受两个参数,一个存储和一个元素。
reduce 函数首先创建一个存储变量。然后,遍历可迭代对象,它使用存储变量和当前元素调用函数,并将存储更新为 returned 值。最后,函数 returns 存储值。最后一个参数是存储变量的开头。
所以在代码片段中,它循环遍历函数数组,并调用其上的相应函数。然后 lambda 将 return 处理后的值,更新存储。
由于您还要求 map
解决方案,这里有一个。我的 values
包含 single-element 可迭代对象,values[0]
具有原始值,values[i]
具有应用第一个 i
函数后的值。
text = ' abCDef '
fs = [str.rstrip, str.lstrip, str.lower]
values = [[text]]
values += map(map, fs, values)
result = next(values[-1])
print(repr(result)) # prints 'abcdef'
但我不推荐这个。我主要是好奇我是否能做到。现在我将尝试考虑如何避免构建辅助列表。
这是一个替代解决方案,它允许您组合任意数量的函数并保存组合函数以供重复使用:
import functools as ft
def compose(*funcs):
return ft.reduce(lambda f, g: lambda x: f(g(x)), funcs)
用法:
In [4]: strip_and_lower = compose(str.rstrip, str.lstrip, str.lower)
In [5]: strip_and_lower(' abCDef ')
Out[5]: 'abcdef'
In [6]: strip_and_lower(" AJWEGIAJWGIAWJWGIWAJ ")
Out[6]: 'ajwegiajwgiawjwgiwaj'
In [7]: strip_lower_title = compose(str.title, str.lower, str.strip)
In [8]: strip_lower_title(" hello world ")
Out[8]: 'Hello World'
请注意,函数的顺序很重要;这就像数学函数组合一样工作,即 (f . g . h)(x) = f(g(h(x))
因此函数从右到左应用。
问题陈述
我想将函数列表 fs = [ f, g, h ]
按顺序应用于字符串 text=' abCdEf '
类似于f( g( h( text) ) )
。
这可以使用以下代码轻松完成:
# initial text
text = ' abCDef '
# list of functions to apply sequentially
fs = [str.rstrip, str.lstrip, str.lower]
for f in fs:
text = f(text)
# expected result is 'abcdef' with spaces stripped, and all lowercase
print(text)
使用functools.reduce
似乎 functools.reduce
应该在这里完成这项工作,因为它在每次迭代时“消耗”函数列表。
from functools import reduce
# I know `reduce` requires two arguments, but I don't even know
# which one to chose as text of function from the list
reduce(f(text), fs)
# first interaction should call
y = str.rstrip(' abCDef ') --> ' abCDef'
# next iterations fails, because tries to call ' abCDef'() -- as a function
不幸的是,这段代码不起作用,因为每次迭代 returns 都是一个 字符串 而不是一个函数,并且失败并返回 TypeError
:'str' object is not callable
.
问题:是否有使用 map
、reduce
或 list comprehension
解决此问题的解决方案?
reduce
可以接受三个参数:
reduce(function, iterable, initializer)
这三个参数一般是什么?
function
是两个参数的函数。我们称这两个参数为t
和f
.- 第一个参数
t
将从initializer
开始;然后将继续作为先前调用function
. 的 return 值
- 第二个参数
f
取自iterable
。
我们案例中的这三个参数是什么?
- 可迭代对象是您的函数列表;
- 第二个参数
f
将成为函数之一; - 第一个参数
t
必须是文本; - 初始化器必须是初始文本;
function
的 return 必须是结果文本;function(t, f)
必须是f(t)
.
最后:
from functools import reduce
# initial text
text = ' abCDef '
# list of functions to apply sequentially
fs = [str.rstrip, str.lstrip, str.lower]
result = reduce(lambda t,f: f(t), fs, text)
print(repr(result))
# 'abcdef'
你可以试试这个:
import functools
text = ' abCDef '
fs = [str.rstrip, str.lstrip, str.lower]
text = functools.reduce(lambda store, func: func(store), fs, text)
print(text)
我认为您误解了 reduce 的工作原理。 Reduce 将可迭代对象减少为单个值。回调函数可以接受两个参数,一个存储和一个元素。
reduce 函数首先创建一个存储变量。然后,遍历可迭代对象,它使用存储变量和当前元素调用函数,并将存储更新为 returned 值。最后,函数 returns 存储值。最后一个参数是存储变量的开头。
所以在代码片段中,它循环遍历函数数组,并调用其上的相应函数。然后 lambda 将 return 处理后的值,更新存储。
由于您还要求 map
解决方案,这里有一个。我的 values
包含 single-element 可迭代对象,values[0]
具有原始值,values[i]
具有应用第一个 i
函数后的值。
text = ' abCDef '
fs = [str.rstrip, str.lstrip, str.lower]
values = [[text]]
values += map(map, fs, values)
result = next(values[-1])
print(repr(result)) # prints 'abcdef'
但我不推荐这个。我主要是好奇我是否能做到。现在我将尝试考虑如何避免构建辅助列表。
这是一个替代解决方案,它允许您组合任意数量的函数并保存组合函数以供重复使用:
import functools as ft
def compose(*funcs):
return ft.reduce(lambda f, g: lambda x: f(g(x)), funcs)
用法:
In [4]: strip_and_lower = compose(str.rstrip, str.lstrip, str.lower)
In [5]: strip_and_lower(' abCDef ')
Out[5]: 'abcdef'
In [6]: strip_and_lower(" AJWEGIAJWGIAWJWGIWAJ ")
Out[6]: 'ajwegiajwgiawjwgiwaj'
In [7]: strip_lower_title = compose(str.title, str.lower, str.strip)
In [8]: strip_lower_title(" hello world ")
Out[8]: 'Hello World'
请注意,函数的顺序很重要;这就像数学函数组合一样工作,即 (f . g . h)(x) = f(g(h(x))
因此函数从右到左应用。