从列表列表中查找元素的位置

时间:2013-03-21 21:32:24

标签: list haskell

我想从列表列表中找到元素的位置。

例如,在给定列表中[[1,2,3],[4,5,6],[7,8,9]]我想找到8的位置。函数应该返回[[ 3,2]],即第三行和第二列。 如果清单是[[1,2,8],[4,5,6],[7,8,9]] 然后它应该返回: [[1,3],[3,2] 如果它找不到那么它应该返回空列表

findPosition :: [[Int]]  ->  [(Int,Int)]
findPostion  ..  ?

我想以最有效的方式做到这一点。 感谢。

4 个答案:

答案 0 :(得分:4)

好的,让我们打破这个。

如果您只对正常的整体列表感兴趣,那么

 findPosition :: [Int] -> [Int]

你怎么能实现呢?好吧,呃,你需要输入你正在搜索的东西!

 findPosition :: Int -> [Int] -> [Int]
好的,很酷。所以内置的elem函数告诉你 if 你想要的元素是什么。但我们希望它是位置。又怎样?好吧,你可以用它的位置“标记”每个元素,如下所示:

 label :: [x] -> [(Int, x)]
 label = zip [0..]

现在我们可以使用filter查找所有项目:

 find :: (Eq x) => x -> [(Int, x)] -> [(Int, x)]
 find x0 = filter (\ (n, x) -> x == x0)

但是我们只想要实际的位置,而不是x s(此时它们都是相同的)。所以我们可以map fst来获得它。

全部组装,

 findPosition :: Int -> [Int] -> [Int]
 findPosition x0 = map fst . filter (\ (n, x) -> x == x0) . zip [0..]

太棒了!但是你想要一个列表的列表,对吧?

我建议您更改您的需求规范,将每个“坐标”作为元组而不是列表返回。就是这样,

findPosition 8 [[1,2,8],[4,5,6],[7,8,9]] => [(1, 3), (3, 2)]

这种方式可能不那么令人困惑。希望我已经给你足够的提示来解决这里的问题...

答案 1 :(得分:4)

您的类型签名错误。它应该是

findPosition :: Eq a => a -> [[a]] -> [(Int, Int)]

,因为

  • 您需要告诉函数要查找的值
  • 没有理由将findPosition限制为仅搜索Int列表的列表---所有与内部元素相关的内容都是为了比较它们的相等性
  • 你的版本将返回一个总长度为2的列表列表:如果内部列表为空或者长度为3,那将是一个错误;我们可以通过使用一对来排除这种错误的可能性

我也会findPosition导致基于零的索引(由Haskell的标准列表函数使用)而不是您要求的基于一的索引。

所以我会有例如findPosition 8 [[1,2,3],[4,5,6],[7,8,9]] = [(2, 1)]


可悲的是,Hoogle知道没有Eq a => a -> [[a]] -> [(Int, Int)]类型的函数。但搜索更简单的签名Eq a => a -> [a] -> [Int](搜索列表而不是列表列表的类似函数)指向elemIndices。我们可以在findPosition中使用它。

(哦,我太累了,不能完成这件事。希望它能让你深思熟虑。)

答案 2 :(得分:4)

import Data.List

findPosition  :: Int -> [[Int]] -> [(Int,Int)]
findPosition n xs = fp n xs 0

fp n [] i = []
fp n (x:xs) i = p x ++ fp n xs (i+1)
    where 
      p x = zip (repeat i) (elemIndices n x)

示例:

findPosition 3 [[2,3,4,3],[4,5,2,3],[],[3,2,5,6,3],[2],[3]]
   == [(0,1),(0,3),(1,3),(3,0),(3,4),(5,0)]

如果您将函数的类型签名更改为:

findPosition :: (Eq a1, Num a) => a1 -> [[a1]] -> [(a, Int)]

你将有一个更通用的解决方案。例如:

findPosition 'a' ["car","small","caveat","big","","aah!"]
   == [(0,1),(1,2),(2,1),(2,4),(5,0),(5,1)]

答案 3 :(得分:1)

可能的解决方案:

import Control.Monad

findPosition :: Eq a => a -> [[a]] -> [(Int,Int)]
findPosition e ll = do
    let annotate = zip [1..]
    (i1,x) <- annotate ll
    (i2,y) <- annotate x
    guard $ y == e
    return (i1,i2)

我们使用索引注释列表和子列表中的每个元素,并使用List的Monad实例搜索所有可能的事件。