给出一个列表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
答案 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
是一项使我们可以同时进行查找和更改的功能。 first
和second
分别在元组的第一和第二元素上映射函数。您实际上可以将此功能重写为折叠:
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
这将alterF
与Either Int
仿函数一起使用。在每个步骤中,您 要么获取存储的索引,要么获得新的地图。