Конкатенация списка Haskell против формата (head: tail)

Я всегда писал свои рекурсивные функции, производящие список, в этом формате:

recursiveFunc :: [a] -> [b]
recursiveFunc (x:xs) = [change x] ++ resursiveFunc xs where
    change :: a -> b
    change x = ...

Я понимаю, что любая функция, подобная приведенной выше, может быть написана для a -> b case, а затем просто maped над набором [a], но, пожалуйста, возьмите эту разбавленную ситуацию в качестве примера.

HLint предлагает заменить [change x] ++ recursiveFunc xs С change x : recursiveFunc xs.

это предложение чисто эстетическое, или есть какой-то эффект на то, как Haskell выполняет функцию?

3 ответов


при использовании [change x] ++ recursiveFunc xs вы создаете лишний список из одного элемента, который затем разбирается


основным преимуществом является то, что change x : blah понятнее -- (:) - это именно то, что вы пытаетесь сделать (добавить один элемент в список). Зачем вызывать две функции, когда одна подойдет?

предлагаемый способ также немного более эффективен - ваш способ создает список из 1 элемента, а затем выбрасывает его и заменяет его другой ссылкой списка. Другой способ просто создает одну ссылку списка. Тем не менее, разница достаточно мала, чтобы быть неважной в 99% случаев.


строку

recursiveFunc (x:xs) = [change x] ++ resursiveFunc xs

немного запутанно, так как он не находится в normalform. Это значит, что ++ можно заменить сразу с одной правой стороной своего определения:

(++) (a:as) bs = a:((++) as bs)
(++) [] bs = bs

-- =>

recursiveFunc (x:xs) = (change x):((++) [] resursiveFunc xs)

-- =>

recursiveFunc (x:xs) = (change x):(resursiveFunc xs)

компилятор Haskell автоматически применяет такое преобразование.

но поскольку это так тривиально это делает ваш код труднее читать. Один смотрит на него и спрашивает: "подожди ... что?'