我正在学习函数式编程的概念并尝试问题练习。 练习, 使用map / reduce反转列表。 我的解决方案:
lists = [ 1, 2, 3, 5 ,6]
def tree_reverse(lists):
return reduce(lambda a, x: a + [a.insert(0,x)], lists, [])
print tree_reverse(lists)
输出:
[6, 5, 3, 2, 1, None, None, None, None, None]
我不明白为什么Nones等于列表中的元素数量。
编辑:针对嵌套列表的案例扩展问题。
lists = [ 1 ,2 ,3 , [5,6,7], 8, [9, 0, 1 ,4 ]]
答案 0 :(得分:4)
起初,我不太了解Python,但是当你将问题标记为"功能编程"并且你说你想学习一般的函数式编程我想对你的问题做一些一般性的解释。
首先,您应该重新考虑map
和reduce
操作的作用以及他们的期望。让我们从map
函数开始。
列表的map
函数(在函数式编程中,您不只有一个映射,每个泛型类型都有一个映射)对每个参数执行一个函数。该函数可以对该单个参数进行操作并对其进行转换。此转换的结果将转换为新列表。一个重要的注意事项是,您传递给map
的函数只能看到一个元素,因此您无法使用map
本身来整体转换列表。您只能转换列表中的每个元素。但是改变顺序需要具有列表中至少两个元素的想法,并且地图只能获得单个元素。这就是反向逻辑不能在map
中并且必须属于reduce
操作的原因。
另一方面,reduce
函数需要一个函数,它接受两个相同类型的东西,并返回一个相同类型的单个项。例如,reduce函数可以采用" +"功能。 " +"是一个函数,需要两个int
并返回一个新的int
。签名基本上是int + int = int
。减少的本质是采取两件事并将其变成一个新项目。例如,如果您有类似于类" Foo"的更复杂的东西,那么您必须提供的功能可以减少签名Foo * Foo -> Foo
的需要。含义将Foo
作为第一个和第二个参数,并返回一个新的Foo
。
同样重要的是要注意reduce
如何使用该功能。我们假设你有一个列表[1,2,3,4,5]
,我们假设你有一个accumulator function
需要两个int
才能将它们加在一起。你能想到会发生什么的是以下几点。
reduce
函数获取列表的前两个参数(在本例中为1和2)accumulator function
(1 + 2 = 3)所以您可以想象会发生什么是以下
[1,2,3,4,5] -> 1 + 2 = 3
[3,3,4,5] -> 3 + 3 = 6
[6,4,5] -> 6 + 4 = 10
[10,5] -> 10 + 5 = 15
[15] -> Return just "15". Not a "[15]"
实际上,内部流程通常有点不同。但这是你可以想象会发生什么的过程。请务必注意,您永远不会修改列表。您始终在该过程中创建新列表。另一个重要说明。在reduce
函数的末尾,您有一个单项的列表。但reduce
函数不返回整个列表。它只返回单个元素。因此,您获得15
int
作为结果。您没有获得包含15
的单个项目的列表。 Reduce将返回单个元素。不管是什么。另一种思考方式。您总能获得accumulator function
的类型。如果您传递的accumulator function
需要两个int
添加它们并返回一个新的int
。您的reduce函数也将返回int
。如果您传递的accumulator function
需要两个Foo
个类并返回一个新的Foo
。您的reduce
函数也会返回Foo
作为结果。 reduce
的返回类型始终与accumulator function
的类型相同。
现在让我们把所有这些碎片放在一起。目标是撤销列表。第一件重要的事情是。您的结果类型为list
。这也意味着您传递到reduce
的函数也必须返回list
。但由于输入始终与输出相同。您现在必须提供一个带有两个列表的accumulator function
,并返回一个新列表。
但现在让我们退一步。如果您使用列表" [1,2,3,4,5]"会发生什么?直接作为输入减少?简短的回答是,它不会起作用。 reduce
会对您的列表提出两个参数。但你拥有的是一个int列表。但是你的accumulator function
期望两个列表不是两个int。要解决这个问题,您现在可以尝试将列表中的每个元素转换为自己的列表。那么我们如何转换列表中的每个元素呢?对! map
!所以你需要做的是以下几点。您首先将列表映射到列表列表中。
[1,2,3,4,5] -> [[1],[2],[3],[4],[5]]
现在你的reduce函数获取第一个列表的前两个元素。这意味着你现在有一个累加器函数,它以[1]
和[2]
为参数。两个单独的列表。但是你的累加器函数必须返回一个新的列表。如果不清楚,只需提醒累加器函数需要什么并返回。
int, int -> int
float,float -> float
Foo,Foo -> Foo
list,list -> list
因此,您现在要做的是将这两个列表合并为一个新列表。或者换句话说,你必须追加/连接这两个列表。我不确切地知道如何在Python中连接两个列表,我们假设这里的操作将是" ++"。因此,如果你只是连接第一个参数和第二个参数,你就不会得到你想要的东西。
[[1],[2],[3],[4],[5]] -> [1] ++ [2] = [1,2]
[[1,2],[3],[4],[5]] -> [1,2] ++ [3] = [1,2,3]
[[1,2,3],[4],[5]] -> [1,2,3] ++ [4] = [1,2,3,4]
[[1,2,3,4],[5]] -> [1,2,3,4] ++ [5] = [1,2,3,4,5]
[[1,2,3,4,5]] -> Return first element [1,2,3,4,5]
所以你要做的就是用第一个参数连接第二个参数。
[[1],[2],[3],[4],[5]] -> [2] ++ [1] = [2,1]
[[2,1],[3],[4],[5]] -> [3] ++ [2,1] = [3,2,1]
[[3,2,1],[4],[5]] -> [4] ++ [3,2,1] = [4,3,2,1]
[[4,3,2,1],[5]] -> [5] ++ [4,3,2,1] = [5,4,3,2,1]
[[5,4,3,2,1]] -> Return first element [5,4,3,2,1]
现在你得到的是你的反向清单。所以你有什么待办事项
例如,在F#中,整个代码看起来像这样。
let list = [1;2;3;4;5]
let reversed =
list
|> List.map (fun x -> [x]) // map every element to a list
|> List.reduce (fun xs ys -> List.append ys xs) // Note ys ++ xs - reversed order for appending
printfn "%A" reversed
// prints: [5;4;3;2;1]
我认为您应该能够将其转换为Python。另行通知。做" map"然后是一个" concat"操作(没有反转)也被命名为" bind"在函数式编程中。
答案 1 :(得分:2)
您的简化操作会将None
追加到列表a
的末尾,因为a.insert(0,x)
将返回None
。
因此,您要就地修改列表a
,但同时附加None
值。
如果您重写reduce
操作以使用for
循环,它看起来像:
def reverse_list(lists):
r = []
for x in lists:
val = r.insert(0,x)
r = r + [val] # val (the return value of r.insert) is None
return r
答案 2 :(得分:0)
如果是嵌套列表。我发帖回答参考。
lists = [ 1 ,2 ,3 , [5,6,7], 8, [9, 0, 1 ,4 ]]
def tree_reverse(lists):
return reduce(lambda a, x: ( map(tree_reverse, [x]) if isinstance(x,list) else [x] ) + a , lists , [] )
print tree_reverse(lists)
输出:
[[4, 1, 0, 9], 8, [7, 6, 5], 3, 2, 1]
答案 3 :(得分:0)
函数式编程语言通常会使用递归来实现迭代。
递归tree_reverse
函数可能看起来像
def tree_reverse(lists):
return tree_reverse(lists[1:]) + [lists[0]] if lists else []
如果你看看Haskell,它可能会像这样实现
reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]
x
是列表的头部(第一个元素),xs
是尾部(列表减去头部),你可以看到reverse'
是递归调用的这些元素反转,反向列表反复建立。