Haskell: функция приложения с $ - PullRequest
11 голосов
/ 05 декабря 2011

В следующем фрагменте вы можете увидеть две мои функции коллатца, которые я написал на Haskell. Для рекурсивного применения я использовал скобки в первом примере (collatz), чтобы получить правильный приоритет.

Поскольку я только что изучил применение функции с помощью $, я попытался переписать функцию (collatz '), используя эту вещь. Однако я сталкиваюсь со следующей ошибкой:

Не удалось найти ожидаемый тип `[a] ' против предполагаемого типа `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]

Я был бы признателен, если бы кто-нибудь взглянул на это и сказал мне, что я делаю неправильно.

Ответы [ 4 ]

18 голосов
/ 05 декабря 2011

$ имеет более низкий приоритет, чем : (а также все остальное), поэтому ваша функция анализируется как

(n : collatz') $ (n `div` 2)

Это приводит к ошибке вашего типа.Второй аргумент : ожидает список, но вместо этого вы передаете функцию collatz.

Если вы все еще хотите избежать скобок вокруг части 3n + 1, вы можете сделать что-то вроде следующего:

(n:) . collatz' $ n `div` 2
n : (collatz' $ n `div` 2)

хотя они не обязательно более чистые, чем оригинал.Если вам интересно, (n:) в первом примере является синтаксическим сахаром для \x -> n : x

10 голосов
/ 05 декабря 2011

Поскольку другие объяснили, в чем проблема, я подумал, что объясню, как вы могли бы выяснить это самостоятельно. (Учить человека ловить рыбу и так далее ...)

Обратите внимание на эту часть сообщения об ошибке:

В первом аргументе '($)', а именно: 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, что объясняет, почему : является обязательным "более жестким", чем $.

6 голосов
/ 05 декабря 2011

: связывается сильнее, чем $. Рассмотрим

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).

3 голосов
/ 05 декабря 2011

Как было сказано @missingno, это проблема приоритета операторов. Вы могли бы переписать это так

collatz' n | even n    = n : (collatz' $ n `div` 2)
           | otherwise = n : (collatz' $ n * 3 + 1)

Но это, очевидно, мало что дает вам, потому что у вас все еще есть скобки.

...