haskell 中的列表的索引列表

Ekn*_*oma 2 indexing haskell concatenation

我正在寻找一种将列表列表与单个列表合并的方法,以保持列表列表的结构。即函数 foo 的工作方式如下:

foo :: [[a]] -> [b] -> [[(a,b)]]

> foo [[1],[2,2,3],[7,8]] [0..]
[[(1,0)],[(2,1),(2,2),(3,3)],[(7,4),(8,5)]]
Run Code Online (Sandbox Code Playgroud)

(或者反过来说)

foo :: [b] -> [[a]] -> [[(b,a)]]

> foo [0..] [[1],[2,2,3],[7,8]]
[[(0,1)],[(1,2),(2,2),(3,3)],[(4,7),(5,8)]]
Run Code Online (Sandbox Code Playgroud)

当然,如果我们知道每个列表的长度相等,则可以通过以下方式轻松创建此函数:

import Data.List.Split (chunksOf)

foo :: [[a]] -> [b] -> [[(a,b)]]
foo xs ys = chunksOf n $ zip (concat xs) ys
  where
    n = length $ head xs
Run Code Online (Sandbox Code Playgroud)

但是,在列表长度不等的更一般情况下,如何完成此功能呢?

lef*_*out 6

您可以使用嵌套遍历:遍历外部列表,遍历每个内部列表,并在每一步弹出另一个列表的元素并将其与当前遍历的元素配对。这可以很好地写在状态单子中:

\n
import Control.Monad.Trans.State\n\nfoo :: \xe2\x88\x80 a b . [[a]] -> [b] -> [[(a,b)]]\nfoo ls ys = evalState nestedTrav ys\n where nestedTrav :: State [b] [[(a,b)]]\n       nestedTrav = forM ls $ \\l ->\n                      forM l $ \\x ->\n                        state $ \\(y:ys) -> (ys, (x,y))\n
Run Code Online (Sandbox Code Playgroud)\n

...或更短

\n
foo ls ys = (`evalState`ys)\n            . forM ls . traverse\n            $ \\x -> state $ \\(y:ys) -> (ys, (x,y))\n
Run Code Online (Sandbox Code Playgroud)\n

仅当第二个列表至少与所有第一个列表一样长(或无限,如您的示例中)时,这才有效。我建议您通过将遍历版本分解为折叠和/或递归来实现一般情况,然后在第二个列表变空时添加中止机制。

\n