Конкатенация списка Haskell против формата (head: tail)
Я всегда писал свои рекурсивные функции, производящие список, в этом формате:
recursiveFunc :: [a] -> [b]
recursiveFunc (x:xs) = [change x] ++ resursiveFunc xs where
change :: a -> b
change x = ...
Я понимаю, что любая функция, подобная приведенной выше, может быть написана для a -> b
case, а затем просто map
ed над набором [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 автоматически применяет такое преобразование.
но поскольку это так тривиально это делает ваш код труднее читать. Один смотрит на него и спрашивает: "подожди ... что?'