Haskell: приложение-функция с $
В следующем фрагменте вы можете увидеть мои две функции collatz, которые я написал в Haskell. Для рекурсивного приложения я использовал круглые скобки в первом примере (collatz), чтобы получить правильный приоритет.
Как я только что изучил функциональное приложение с $, я попытался переписать функцию (collatz '), используя эту вещь. Однако я сталкиваюсь со следующей ошибкой:
Не удалось сопоставить ожидаемый тип `[a] ' против inferred type `a1 → [a1] 'Во втором аргументе` (:)', а именно `collatz '' В первом аргументе` ($) ', а именно `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]
Я был бы признателен, если бы кто-нибудь мог взглянуть на него и сказать мне, что я делаю неправильно.
Ответы
Ответ 1
$
имеет более низкий приоритет, чем :
(а также все остальное), поэтому ваша функция анализируется как
(n : collatz') $ (n `div` 2)
Это приведет к ошибке вашего типа. Второй аргумент :
ожидает список, но вместо этого вы передаете функцию collatz.
Если вы все еще хотите избежать скобок вокруг части 3n + 1, вы можете сделать что-то вроде следующего
(n:) . collatz' $ n `div` 2
n : (collatz' $ n `div` 2)
хотя они не обязательно более чистые, чем оригинал. В случае, если вам интересно, (n:)
в первом примере представляет собой синтаксический сахар для \x -> n : x
Ответ 2
Поскольку другие объяснили, в чем проблема, я подумал, что объясню, как вы могли разобраться в этом сами. (Обучение человека рыбе и т.д.)
Обратите внимание на эту часть сообщения об ошибке:
В первом аргументе '($)', а именно '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, что объясняет, почему :
является обязательным "более жестким", чем $
.
Ответ 3
:
связывается сильнее, чем $
. Рассмотрим
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)
.
Ответ 4
Как указано в @missingno, это проблема приоритета оператора. Вы можете переписать его так:
collatz' n | even n = n : (collatz' $ n `div` 2)
| otherwise = n : (collatz' $ n * 3 + 1)
Но это явно не покупает вас много, потому что у вас все еще есть скобки.