Haskell: функциональное приложение с $
Вопрос
В следующем фрагменте вы можете увидеть две мои функции Collatz, которые я написал в Haskell. Для рекурсивного применения я использовал скобки в первом примере (Collatz), чтобы получить правильный приоритет.
Поскольку я только что выучил приложение для функций с $, я попытался переписать функцию (коллатц), используя эту вещь. Однако я сталкиваюсь с следующей ошибкой:
Не мог соответствовать ожидаемому типу `[a] 'против предполагаемого типа` a1 -> [a1]' во втором аргументе «(:)», а именно «коллатц» в первом аргументе «($)», а именно `n: collatz '' в выражении: n: collatz '$ n` div` 2
collatz :: (Integral a) => a -> [a]
collatz 1 = [1]
collatz n | even n = n : collatz (n `div` 2)
| otherwise = n : collatz (n * 3 + 1)
collatz' :: (Integral a) => a -> [a]
collatz' 1 = [1]
collatz' n | even n = n : collatz' $ n `div` 2
| otherwise = n : collatz' $ n * 3 + 1
Мне странно, что это не сработало. Итак, я попробовал аналогичный пример, который сработал:
True : [even $ 3 `div` 3]
Я бы признателен, если бы кто -то мог взглянуть на это и сказать мне, что я делаю не так.
Решение
$
Тогда имеет более низкий приоритет :
(а также все остальное), поэтому ваша функция анализирует как
(n : collatz') $ (n `div` 2)
Это приводит к вашей ошибке типа. Второй аргумент :
Ожидает список, но вместо этого вы выполняете функцию Collatz.
Если вы все еще хотите избежать скобок вокруг 3n+1 части, вы можете сделать что -то вроде следующего
(n:) . collatz' $ n `div` 2
n : (collatz' $ n `div` 2)
Хотя это не обязательно чище, то оригинал. Если вам интересно, (n:)
В первом примере - синтаксический сахар для \x -> n : x
Другие советы
Поскольку другие объяснили, в чем проблема, я подумал, что объясню, как вы могли бы понять это самостоятельно. (Учите человека ловить рыбу и так далее ...)
Обратите внимание на эту часть сообщения об ошибке:
В первом аргументе «($)», а именно «n: collatz»
Это подсказка в том, чтобы заметить, что это проблема приоритета. GHC говорит вам, что n : collatz'
был проанализирован как первый аргумент $
, пока вы ожидали, что первый аргумент будет просто collatz'
.
На этом этапе я обычно запускаю GHCI и проверяю приоритеты, включенные с использованием :info
Команда:
> :info :
data [] a = ... | a : [a] -- Defined in GHC.Types
infixr 5 :
> :info $
($) :: (a -> b) -> a -> b -- Defined in GHC.Base
infixr 0 $
В нем говорится, что достоверность :
5, в то время как приоритет $
0, что объясняет, почему :
является обязательным «плотнее», чем $
.
:
связывается более сильно, чем $
. Анкет Рассмотреть возможность
Prelude> let f x = [x]
Prelude> 1 : f 2
[1,2]
Prelude> 1 : f $ 2
<interactive>:1:5:
Couldn't match expected type `[a0]' with actual type `t0 -> [t0]'
In the second argument of `(:)', namely `f'
In the expression: 1 : f
In the expression: 1 : f $ 2
Обратите внимание на «выражение» 1 : f
найдено парсером; он видит (1 : f) $ 2
скорее, чем 1 : (f $ 2)
.
Как заявил @missingno, это проблема приоритета оператора. Вы можете переписать это так
collatz' n | even n = n : (collatz' $ n `div` 2)
| otherwise = n : (collatz' $ n * 3 + 1)
Но это, очевидно, не покупает вас много, потому что у вас все еще есть скобка.