如何按偶数和奇数位置将列表拆分为两个列表?

How to split a list into two lists by even and odd positions?

我想按位置将任意元素列表拆分为两个包含所有偶数元素和奇数元素的新列表。

示例: 使用这样的列表:

["a", "b", "c", "d", "e"]

如何获得两个这样的列表:

(["a", "c", "e"], ["b", "d"])

解决方案 1:

evensAndOdds : List a -> (List a, List a)
evensAndOdds items
  let
    enum = List.indexedMap Tuple.pair items
    evens = List.filterMap (\(i, v) -> if modBy 2 i == 0 then Just v else Nothing) enum
    odds = List.filterMap (\(i, v) -> if modBy 2 i == 1 then Just v else Nothing) enum
  in
    (evens, odds)

解决方案 2:

evensAndOdds : List a -> (List a, List a)
evensAndOdds =
  List.indexedMap (\i v -> (modBy 2 i == 0, v))
  >> List.partition Tuple.first
  >> Tuple.mapFirst (List.map Tuple.second)
  >> Tuple.mapSecond (List.map Tuple.second)

一次通过,代码更少:

evensAndOdds : List a -> (List a, List a)
evensAndOdds =
  List.foldr (\item (a, b) -> (item :: b, a)) ([], [])

这里的技巧是在每次迭代时切换返回元组元素的位置,从而交替追加到哪个元素,而不必跟踪索引。

你也可以使用递归函数。它可能比 fold 更容易阅读,特别是对于初学者。

evensAndOdds : List a -> ( List a, List a )
evensAndOdds input =
    case input of
        [] ->
            ( [], [] )

        [ even ] ->
            ( [ even ], [] )

        [ even, odd ] :: tail ->
            let
                ( tailEvens, tailOdds ) =
                    evensAndOdds tail
            in
            ( even :: tailEvens, odd :: tailOdds )