使用map / reduce反转列表

时间:2015-11-25 05:10:34

标签: python dictionary functional-programming reduce

我正在学习函数式编程的概念并尝试问题练习。 练习, 使用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 ]]

4 个答案:

答案 0 :(得分:4)

起初,我不太了解Python,但是当你将问题标记为"功能编程"并且你说你想学习一般的函数式编程我想对你的问题做一些一般性的解释。 首先,您应该重新考虑mapreduce操作的作用以及他们的期望。让我们从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才能将它们加在一起。你能想到会发生什么的是以下几点。

  1. reduce函数获取列表的前两个参数(在本例中为1和2)
  2. 将这些传递给accumulator function(1 + 2 = 3)
  3. 并用结果替换列表中的两个参数。 [3,3,4,5]
  4. 返回1)并重复该过程,直到您的列表只有一个项目
  5. 返回该单项
  6. 所以您可以想象会发生什么是以下

    [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]
    

    现在你得到的是你的反向清单。所以你有什么待办事项

    1. 将每个元素映射到列表。所以你得到一份清单
    2. 使用reduce将每个列表列表以相反的顺序连接到一个新列表中。
    3. 例如,在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'是递归调用的这些元素反转,反向列表反复建立。