Pairs of elements from list

前端 未结 2 459
粉色の甜心
粉色の甜心 2021-01-18 10:26

I want to convert [1,2,3,4] to [[1 2] [2 3] [3 4]] or [(1 2) (2 3) (3 4)]. In clojure I have (partition 2 1 [1,2,3,4]). H

相关标签:
2条回答
  • 2021-01-18 10:36

    Just to throw another answer out there using a different approach:

    For n=2 you want to simply zip the list with its tail. For n=3 you want to zip the list with its tail and with the tail of its tail. This pattern continues further, so all we have to do is generalise it:

    partition n = sequence . take n . iterate tail
    

    But this only works for an offset of 1. To generalise the offsets we just have to look at the genrated list. It will always have the form:

    [[1..something],[2..something+1],..]
    

    So all left to do is select every offsetth element and we should be fine. I shamelessy stole this version from @ertes from this question:

    everyNth :: Int -> [a] -> [a]
    everyNth n = map head . takeWhile (not . null) . iterate (drop n)
    

    The entire function now becomes:

    partition size offset = everyNth offset . sequence . take size . iterate tail
    
    0 讨论(0)
  • 2021-01-18 10:48

    The standard trick for this is to zip the list with it's own tail:

    > let xs = [1,2,3,4] in zip xs (tail xs)
    [(1,2),(2,3),(3,4)]
    

    To see why this works, line up the list and its tail visually.

          xs = 1 : 2 : 3 : 4 : []
     tail xs = 2 : 3 : 4 : []
    

    and note that zip is making a tuple out of each column.

    There are two more subtle reasons why this always does the right thing:

    • zip stops when either list runs out of elements. That makes sense here since we can't have an "incomplete pair" at the end and it also ensures that we get no pairs from a single element list.
    • When xs is empty, one might expect tail xs to throw an exception. However, because zip checks its first argument first, when it sees that it's the empty list, the second argument is never evaluated.

    Everything above also holds true for zipWith, so you can use the same method whenever you need to apply a function pairwise to adjacent elements.

    For a generic solution like Clojure's partition, there is nothing in the standard libraries. However, you can try something like this:

    partition' :: Int -> Int -> [a] -> [[a]]
    partition' size offset
      | size <= 0   = error "partition': size must be positive"
      | offset <= 0 = error "partition': offset must be positive"
      | otherwise   = loop
      where
        loop :: [a] -> [[a]]
        loop xs = case splitAt size xs of
                    -- If the second part is empty, we're at the end. But we might
                    -- have gotten less than we asked for, hence the check.
                    (ys, []) -> if length ys == size then [ys] else []
                    (ys, _ ) -> ys : loop (drop offset xs)
    
    0 讨论(0)
提交回复
热议问题