列表的唯一元素和相应的索引

时间:2019-04-20 16:17:51

标签: haskell unique

给出一个列表vs,我想获取vs'的唯一元素的列表vs以及{中vs的元素的索引{1}}。例如,如果vs'我想获得vs = [7, 8, 7, 8, 9](唯一元素)和[7,8,9](索引)。

一个简单的实现是:

[0,1,0,1,2]

有没有更有效的选择?

我已经在可变向量的帮助下实现了以下伪代码(假设import Data.List import Data.Maybe unique :: Eq a => [a] -> ([a], [Int]) unique vs = (vsnub, indices) where vsnub = nub vs indices = map (\v -> fromJust $ elemIndex v vsnub) vs 是数字列表)的实现,但这很慢:

vs

编辑

这是我的使用可变向量的代码(慢):

n = length of vs
idx = list of n integers (to store the indices)
visited = [false, false, ..., false] (n elements)
nvs = list of n numbers (to store the unique elements)
count = 0 
for(i = 0; i < n; ++i)
{
  if(not visited[i])
  {
    nvs[count] = vs[i]
    idx[i] = count
    visited[i] = true
    for(j = i+1; j < n; ++j)
    {
      if(vs[j] = vs[i])
      {
        visited[j] = true
        idx[j] = count
      }
    }
    count ++
  }
}
nvs = first 'count' elements of nvs

2 个答案:

答案 0 :(得分:3)

这样的事情有助于将重点放在您实际需要维护的状态上,然后将其编码为递归函数的额外参数。对于这种情况,您需要:

  • 您到目前为止所看到的元素(及其位置)。
  • 您在输出列表中的位置。

其中第一个可以用Map封装,第二个可以用Int封装。

uniqInds :: Ord a => [a] -> ([a], [Int])
uniqInds = go 0 Map.empty
  where
    go i m [] = ([],[])
    go i m (x:xs) = case Map.insertLookupWithKey (\_ _ j -> j) x i m of
        (Nothing, m') -> first (x:) (second (i:) (go (i+1) m' xs))
        (Just j , m') ->             second (j:) (go i     m' xs)

Map.insertLookupWithKey是一项使我们可以同时进行查找和更改的功能。 firstsecond分别在元组的第一和第二元素上映射函数。您实际上可以将此功能重写为折叠:

uniqInds :: Ord a => [a] -> ([a], [Int])
uniqInds xs = foldr f (\_ _ -> ([],[])) xs 0 Map.empty
  where
    f x xs i m = case Map.insertLookupWithKey (\_ _ j -> j) x i m of
        (Nothing, m') -> first (x:) (second (i:) (xs (i+1) m'))
        (Just j , m') ->             second (j:) (xs i     m') 

答案 1 :(得分:3)

这是oisdk解决方案的一种变体,它使用alterF而不是较旧的,功能较弱且陌生的insertLookupWithKey

import qualified Data.Map.Strict as M
import Data.Bifunctor

uniqInds :: Ord a => [a] -> ([a], [Int])
uniqInds = go 0 M.empty
  where
    go i m [] = ([],[])
    go i m (x:xs) = case M.alterF chg x m of
       Right m' -> bimap (x:) (i:) (go (i+1) m' xs)
       Left j -> second (j:) (go i m xs)
      where
        -- The value wasn't found. Insert its index.
        chg Nothing = Right (Just i)
        -- The value was found. Return its index.
        chg (Just j) = Left j

这将alterFEither Int仿函数一起使用。在每个步骤中,您 要么获取存储的索引,要么获得新的地图。