2016-12-04 5 views
1

У меня есть простая функция и желание разобраться в стиле без очков.Можно ли сделать эту рекурсивную функцию свободной?

shout :: String -> String 
shout input 
    | null input = [] 
    | otherwise = (toUpper . head $ input) : (shout . tail $ input) 

Моей интуиция привела меня к этому

pfShout :: String -> String 
pfShout = (toUpper . head) : (shout . tail) 

который жалуется следующим за первый аргумент минусов клетки

не мог соответствовать ожидаемому типу «String -> Строка ' с фактическим типом' [[Char] -> Char] '

  • Возможная причина: '(:)' применяется к слишком большому количеству аргументов

    В выражении: (toUpper. голова):. (pfShout хвост)

    В уравнении для 'pfShout': pfShout = (ToUpper головка):.. (pfShout хвост)

и жаловался об этом на второй аргумент против клеток

не удалось соответствует ожидаемому типа [[Char] -> Char] с фактическим типом '[Char] -> Строка'

  • Возможная причина: '(.)' Применяется слишком мало аргументов

    Во втором аргументе '(:), а именно: «(pfShout. хвост)»

    В выражении: (ToUpper голова):.. (pfShout хвост)

    В уравнении для 'pfShout': pfShout = (ToUpper головка):.. (pfShout хвост)

Это мне ясно, что я не могу сделать список из «String -> String» функции и '[[Char] -> Char], и я начинаю, чтобы получить к месту, где я думаю, что это просто не будет работать безотлагательно.

Я понимаю, что здесь есть другие соображения (например, сейчас у меня нет базового футляра), но. Я также понимаю, что могу полностью переписать функцию для достижения такого же эффекта (например, map toUpper). Я в первую очередь интересуюсь point-free с использованием рекурсии с функцией, как написано.

Если есть (или нет), можно написать эту функцию без точек, что мне не хватает?

+5

бы 'рупор = карта toUpper' работает? –

+0

Ох, есть определенные способы полностью настроить его, но мне было интересно, могу ли я сделать рекурсивную версию без слов (поскольку я печатаю это, я думаю «возможно, рекурсия - это то, почему я не могу указать»). Shoulda сделал это более явным в моем вопросе – Matt

+0

Предположим, что вам удалось исправить '(toUpper. Head): (крик. Хвост)' (это возможно). Как заканчивается рекурсия? Вам нужно оценить какое-то условие. Как вы пишете условие беспроблемным образом? –

ответ

7

As @ n.m отметил, что вы можете использовать shout = map toUpper. Однако это можно сделать без map или любых других причудливых функций, таких как foldr, но нам нужно больше комбинаторов. Нам нужно что-то, что принимает наш входной аргумент и передает его двум функциям toUpper . head и shout . tail, а затем объединяет их с :.Вы Propably не знаете эту функцию еще, но <*> оператор из Applicative имеет то, что нам нужно:

(f <*> g) x = f x (g x) 

Теперь мы можем сделать что-то вроде этого:

combine . f <*> g = \x -> combine (f x) (g x) -- [1] 

Я дам вам понять, как чтобы применить это к вашей проблеме. ;)

Но нам все равно нужно выразить пустой случай в виде. Существует несколько способов сделать это, но самым простым будет bool от Data.Bool функция, которая похожа на функцию if, а также join от Control.Monad.

-- [2] 
bool x _ False = x 
bool _ x True = x 

join f x = f x x 

Теперь мы можем сделать следующее:

shout = join $ bool (not null case) (null case) . null 
-- Which translates to 
shout xs = bool ((not null case) xs) ((null case) xs) (null xs) 

снова реализует два случая оставляется как физические упражнения для читателя.

[1]: Вместо (.) вы можете также использовать (<$>) который для функций такой же, как (.) но (<$>) и (<*>) рода принадлежат друг другу. Вы поймете, почему, как только вы узнаете о применении.

[2]: Если вам интересно, что обоснование порядка аргументов bool есть, первый аргумент является False случай, потому что Bool определяется следующим образом:

data Bool = False | True 

И этот порядок мотивировано по соглашению, что False < True. maybe и either - две другие функции, которые разделяют этот точный шаблон с bool.

+0

Yup, я вижу в моей книге еще несколько разделов. Спасибо за очень тщательный ответ! – Matt

+0

Работает ли «аппликативный» случай? Я работал над ответом по этим строкам, но я не могу получить что-то такое. 'shout = (:) <$> (toUpper.head) <*> (крик. хвост)' "работает" (по крайней мере, для бесконечных списков), но попытка совместить это с результатом 'bool' приводит к ошибке типа. 'shout = bool ((:) <$> (toUpper. head) <*> (крик. хвост)) id. null'. – chepner

+0

@chepner Вам просто не хватает 'join'. 'Bool' потребляет аргумент для предиката, и тогда нет ничего, чтобы применить функции этих двух случаев. – jpath

2

Чтобы переписать что-либо в стиле без очков, установите pointfree или используйте любую из онлайн-версий (http://pointfree.io или https://blunt.herokuapp.com).

Ваше выражение

\input -> (toUpper . head $ input) : (shout . tail $ input) 

переводит

ap ((:) . toUpper . head) (shout . tail) 

(Вы можете заменить <*> для ap, они являются взаимозаменяемыми в данном случае).

Но этого недостаточно. Вам также необходимо как-то закончить рекурсию. Чтобы сделать это в стиле pointfree, вам нужна точка if или совпадение шаблонов без очков, которые, похоже, не существуют в Haskell. В теории if можно было бы определить как встроенную функцию, которая сделала бы возможным определение точки, но в Haskell это не так. (Можно определить такую ​​функцию, но ее реализация не будет pointfree. Таким образом, вы можете торговать map для Data.Bool.bool, но есть точка?

комбинаторы как . и $ и даже ap, вероятно, внутренне не pointfree либо, но используя их не чувствую как обман.Они имеют дело только с функциями и, таким образом, чувствуют себя как-то более фундаментальными, чем bool или map.

Смежные вопросы