f x
- это вызов функции f
с аргументом x
. Но вам не нужно вызывать эту функцию, если тестовое выражение x
уже все, что вам нужно:
isPalindrome :: String -> Bool
isPalindrome s
-- f x
| {- isPalindrome -} ((null s) || (length s == 1)) -- here
= True
| {- isPalindrome -} ((s !! 0) /= (s !! (length s - 1))) -- and here
= False
| otherwise = isPalindrome (tail (init s))
isPalindrome :: String -> Bool
означает, что первый аргумент isPalindrom
, как ожидается, будетString
. Но на самом деле подразумевается логическое значение, используемое в качестве защиты, для выбора подходящего курса действий. Отсюда и сообщение об ошибке. У нас уже есть это Bool
.
Вызов функции в последней строке - это рекурсивный вызов, который действительно должен быть выполнен.
{- ... -}
- это многострочные комментарии в Haskell.
Обычный, более идиоматический способ Haskell - не выполнять эти тесты явным образом, а организовывать эквивалентное сопоставление с шаблоном в определении функции с помощью предложений:
isPalindrome :: String -> Bool
isPalindrome [] = True -- (null s)
isPalindrome [_] = True -- (length s == 1)
isPalindrome -- [a, ..., b] | a /= b
(x:xs) | x /= last xs
= False -- ((s !! 0) /= (s !! (length s - 1)))
isPalindrome -- [a, ...xs, b]
(_:xs) = isPalindrome -- xs
(init xs)
Приведенный выше код содержит некоторыемнимые шаблоны списков в комментариях и их эквиваленты в Haskell в самом коде.
На самом деле, как указывает @ chepner в комментариях, шаблоны часто помогают избежать неэффективности: вычисление length
равно O (n) , тогда как сопоставление с шаблономс [_]
равно O (1) .
Конечно, этот конкретный код все еще очень неэффективен, так как два других пункта также выполняют операции O (n) (last
и init
). Существует алгоритм O (n) .