2015-08-29 2 views
4

Я знаю, что используя. оператор сети функции вместе, как так:Кто может объяснить эту загадку Haskell?

isLessThanZero x 
    | x < 0 = True 
    | otherwise = False 

(isLessThanZero . negate) 3 -- Produces True 

Или с $:

getNegNumbers x = map (*x) [-1, -2, -3] 

filter isLessThanZero $ getNegNumbers 2 -- Produces [-2, -4, -6] 

Но если бы я сделать что-то вроде:

(subtract . negate) 1 2 -- 3 
negate $ subtract 1 2 -- -1 

Результаты здесь разные, и это не имеет смысла, поскольку две функции принимают различное количество аргументов. С . функция negate проверяет, является ли число отрицательным или нет, но предоставляются два аргумента. Это может означать, что выражение лево-ассоциативное.

negate (subtract 1 2) -- -1 
(subtract . negate) 1 2 -- 3 

Но это сбивает с толку, потому что в первом примере:

(isLessThanZero . negate) 3 

выражение производит True, что означает, что функция negate была выполнена первая, то isLessThanZero называется. Но в последнем примере оказалось, что сначала был вызван subtract, а затем был вызван negate. Поэтому я не уверен, что здесь происходит. Но это то, что еще более запутанной:

subtract 1 2 -- Produces 1! 

Что означает, что все выражение:

(subtract . negate) 1 2 -- Produces 3! 

является побочным эффектом использования функции цепочки.

Моя теория заключается в следующем, разлагая таким образом:

Мы знаем, что 2 - (-1) = 3. Таким образом, выражение еще правоассоциативной .. я думаю. negate по-прежнему называется первым, как в предыдущем примере, только потому, что он влияет на первый аргумент, а не на оба аргумента, что имеет смысл, потому что negate принимает только один аргумент, и мы вообще не отображаем функцию.

Итак, когда дело доходит до цепочки функций с различным количеством аргументов, как Haskell реагирует на это?

+1

Вы можете найти ассоциативность и приоритет любого оператора с ': i' в GHCi:': я . 'возвращает вас' [...] infixr 9 .'. –

ответ

2

Это может означать, что выражение лево-ассоциативное.

Я не понимаю, что вы подразумеваете под «менее ассоциативным» здесь.


Функция приложение левоассоциативно, то есть a b c разбирает, как (a b) c.

Состав определяется следующим образом: (a . b) c = a (b c).

Таким образом

(subtract . negate) 1 2 
    =      -- function application associates to the left 
((subtract . negate) 1) 2 
    =      -- definition of composition 
(subtract (negate 1)) 2 
    =      -- definition of subtract 
2 - (negate 1) 
    =      -- definition of negate 
2 - (-1) 
    =      -- definition of - 
3 

Другое выражение выглядит следующим образом (с a $ b = a b):

negate $ subtract 1 2 
    =      -- function application has higher precedence than any operator 
negate $ (subtract 1 2) 
    =      -- definition of $ 
negate (subtract 1 2) 
    =      -- definition of negate 
-(subtract 1 2) 
    =      -- definition of subtract 
-1 
+0

Таким образом, оба являются составными функциями '.' и функции application' $ 'right-associative then (идет справа налево)? – Poriferous

+0

@Poriferous Для '.' это не имеет значения, потому что' a. (b. c) = (a. b). c'. '$' является правильным ассоциативным, потому что 'a $ b $ c' анализирует как' a $ (b $ c) '. – melpomene

3

На Haskell, функции кэрри, так:

f :: a -> b -> c 

так же, как:

f :: a -> (b -> c) 

Итак, когда вы рассчитываете f 1 2, вы первым применением a к f и получить функцию с типом b -> c. И затем вы примените b и получите c.

Теперь давайте следовать типы:

Prelude> :t (.) 
(.) :: (b -> c) -> (a -> b) -> a -> c 

Prelude> :t negate 
negate :: Num a => a -> a 

Prelude> :t subtract 
subtract :: Num a => a -> a -> a 

Prelude> :t (subtract.negate) 
(subtract.negate) :: Num b => b -> b -> b 

Итак, что же тип (subtract.negate) 2?

Prelude> :t (subtract.negate) 1 
(subtract.negate) 1 :: Num b => b -> b 

Как вы можете видеть, negate получил 1 и дал -1, но subtract принял это -1 и дал Int -> Int. А затем вы применили 2 к этому Int -> Int и получили 3.

Вскоре (.) всегда прав-зависим и принимает (b -> c) и (a -> b). Единственная сложная часть состоит в том, что c сам может быть (d -> e).

4

Подход Haskell должен рассматривать все функции как принимающие один аргумент и возвращающие одно значение, даже для функций с несколькими аргументами.

Таким образом, subtract функция, подпись:

subtract :: Num a => a -> a -> a 

могут также рассматриваться:

subtract :: Num a => a -> (a -> a) 

функция, которая принимает числовой аргумент и возвращает функцию, которая принимает одно числовое значение и возвращает числовое значение.

Учитывая (.), его подпись:

(.) :: (y -> z) -> (x -> y) -> x -> z 

Она занимает две функции а возвращает функцию.

Если применить его к (subtract . negate), у вас есть эта подпись:

(subtract . negate) :: Num a => (a -> (a -> a)) -> (a -> a) -> (a -> (a -> a)) 

где:

x = a 
y = a 
z = a -> a 

Сигнатура эта функция вполне допустимо.

Отметьте, что subtract 1 2 действует как 2 - 1.

Функция (subtract . negate) - функция, которая принимает одно числовое значение, отрицает ее и возвращает функцию, которая принимает другое числовое значение, из которого вычитается отрицательное значение.

Обратите внимание, что negate (subtract 1 2) равен -1, а не 3!

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