Haskell - индексация списка - PullRequest
0 голосов
/ 02 мая 2018

У меня есть список из 3 элементов кортежей, я хотел бы проиндексировать список на основе первого элемента, я уже написал код, который звучит логически логично для меня, но я получаю ошибку типа, вот что я написал

addIndex [] indexed = indexed
addIndex ((a1,b1,c1):xs) [] 
                    = addIndex xs [(a1,b1,c1,0)]
addIndex ((a1,b1,c1):xs) indexedWIP 
                    = addIndexH ((a1,b1,c1):xs) indexedWIP (last indexedWIP)
addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix)
                    = if (a1 /= ax) 
                        then (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix+1)))) 
                        else (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix))))

Я получаю следующую ошибку типа

ERROR file:.\lmaogetrektson.hs:109 - Type error in application
*** Expression     : indexedWIP ++ (a1,b1,c1,ix + 1)
*** Term           : (a1,b1,c1,ix + 1)
*** Type           : (b,c,d,e)
*** Does not match : [a]

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Я думаю, вы делаете это более сложным, чем необходимо. Если я правильно понимаю, вы берете в качестве входных данных список из 3-х кортежей (a, b, c), и вы хотите вернуть список из 4-х кортежей (a, b, c, i), где i указывает количество различных a - значения, которые мы наблюдали.

Таким образом, мы выполняем какое-то отображение, но с аккумулятором. Хотя здесь мы можем использовать конструкции более высокого порядка, давайте нацелимся на использование рекурсии и добавление аккумулятора. Сначала мы можем определить вспомогательную функцию с подписью:

addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]

где первый параметр, таким образом, является a -значением предыдущего элемента (здесь мы предполагаем, что мы уже обработали элемент). Второй параметр - это количество элементов, которые мы наблюдали до сих пор, третий элемент - это список элементов, которые нам еще предстоит обработать, а результат - список из 4-х кортежей.

Если список исчерпан, мы можем вернуть пустой список независимо от других переменных:

addIndex' _ _ [] = []

в другом случае, мы должны сравнить предыдущий ключ ap с текущим ключом a, и в случае, если они равны, мы возвращаем кортеж с индексом i в качестве последнего элемента, затем рекурсивно с таким же индексом ; в противном случае мы увеличиваем индекс (до i1 = i + 1). Мы каждый раз повторяем в конце списка:

addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
                               | otherwise = (a, b, c, i1) : addIndex' a i1 xs
    where i1 = i + 1

Итак, мы получаем функцию:

addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]
addIndex' _ _ [] = []
addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
                               | otherwise = (a, b, c, i1) : addIndex' a i1 xs
    where i1 = i + 1

Но теперь нам еще нужно обработать первый элемент. Мы знаем, что если список пуст, мы возвращаем пустой список:

addIndex [] = []

в противном случае мы возвращаем в качестве первого кортежа первый в данном списке с индексом 0, а затем вызываем addIndex' с оставшимися кортежами и первым ключом в качестве аккумулятора:

addIndex ((a, b, c): xs) = (a, b, c, 0) : addIndex' a 0 xs

поэтому получаем полное решение:

addIndex :: (Num n, Eq a) => [(a, b, c)] -> [(a, b, c, n)]
addIndex [] = []
addIndex ((a, b, c): xs) = (a, b, c, 0) : addIndex' a 0 xs

addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]
addIndex' _ _ [] = []
addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
                               | otherwise = (a, b, c, i1) : addIndex' a i1 xs
    where i1 = i + 1

Затем мы генерируем, например:

Prelude> addIndex [('a', 1, 4), ('a', 2, 5), ('b', 1, 3), ('b', 0, 2), ('c', 1, 2)]
[('a',1,4,0),('a',2,5,0),('b',1,3,1),('b',0,2,1),('c',1,2,2)]

Но обратите внимание, что мы смотрим только на предыдущий элемент, и, например, например, если после 'c' появляется клавиша 'a', мы снова увеличим счетчик:

Prelude> addIndex [('a', 1, 4), ('a', 2, 5), ('b', 1, 3), ('b', 0, 2), ('c', 1, 2), ('a', 3, 4)]
[('a',1,4,0),('a',2,5,0),('b',1,3,1),('b',0,2,1),('c',1,2,2),('a',3,4,3)]

Эта функция будет работать в линейном времени O (n) , тогда как составленные вами функции будут выполняться в квадратичном времени O (n * 1056) * 2 ) , поскольку добавление выполняется за линейное время (а также last и т. Д.).

0 голосов
/ 02 мая 2018

Позвольте мне рассмотреть типы ваших addIndex в каждой строке:

addIndex :: [a] -> b -> b
addIndex [] indexed = indexed

-- Combined with the above, leads to:
addIndex :: (Num n) => [(a,b,c)] -> [(a,b,c,n)] -> [(a,b,c,n)]
addIndex ((a1,b1,c1):xs) [] = addIndex xs [(a1,b1,c1,0)]

-- This call demands addIndexH satisfies:
addIndexH :: (Num n) => [(a,b,c)] -> [(a,b,c,n)] -> (a,b,c,n) -> [(a,b,c,n)]
-- It's also costly, as last needs to traverse the list
addIndex ((a1,b1,c1):xs) indexedWIP = 
    addIndexH ((a1,b1,c1):xs) indexedWIP (last indexedWIP)

-- /= check matches types of a1 and ax, requiring them to be Eq
addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix) = 
    if (a1 /= ax) then (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix+1)))) 
    else (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix))))

Различие между списком и кортежем - фактически проблема, с которой вы столкнулись.

Prelude> :t (++)
(++) :: [a] -> [a] -> [a]

Оба операнда на ++ должны быть списками одного типа. Итак, нам нужно что-то вроде:

addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix) = 
    if (a1 /= ax) then (addIndex xs (indexedWIP ++ [(a1,b1,c1,(ix+1))])) 
    else (addIndex xs (indexedWIP ++ [(a1,b1,c1,(ix))]))

Конечным результатом должна быть функция, которая принимает список из 3-х кортежей и другой список из перечисленных 4-кортежей, но довольно окольным образом. Посмотрите, как он расширяется:

addIndex [(a,b,c), (x,y,z)] []
addIndex [(x,y,z)] [(a,b,c,0)]
addIndexH [(x,y,z)] [(a,b,c,0)] (a,b,c,0)
addIndex [] ([(a,b,c,0)] ++ [(x,y,z,(0+1))])
([(a,b,c,0)] ++ [(x,y,z,(0+1))])

Это довольно сложная процедура, и она становится тем хуже, чем длиннее списки (мы даже не рассматривали дублирование полей).

Когда вы встречаете дубликат поля, вы все равно добавляете его, сохраняя только новое значение индекса. Это означает, что, поскольку мы проверили только последний элемент, у нас есть два элемента, соответствующие a и индексам, расположенные рядом друг с другом. Функцию можно переписать несколькими способами, в частности, без перестройки списков каждой промежуточной длины и обхода растущего списка для каждого элемента.

...