Составление функций с более высокой степенью

Я не понимаю составные функции с arity > 1. в ghci 7.4.1 я напечатал:

((*).succ) 3 4
> 16

Я не полностью понимаю математическое преобразование, но ясно, что оно такое же, как

(*) (succ 3) 4

но когда я делаю:

( (\x y z -> x).(\a b -> a*b) ) 2 3 4 5
> 10
( (\x y z -> y).(\a b -> a*b) ) 2 3 4 5
> No instance for (Num (a0 -> t0))

и теперь я полностью потерян. может ли кто-нибудь объяснить, что происходит? пс. Я знаю, что все в haskell имеет только один параметр, но мне это действительно не помогает:)

Ответ 1

Разработайте так:

(f . g) x = f (g x)
(f . g) x y = f (g x) y -- applying y

Затем замените f на (*), g с помощью succ и x и y с их значениями:

((*) . succ) 3 4 = (*) (succ 3) 4
                 = (*) 4 4
                 = 16

Ответ 2

Когда вы составляете (\x y z -> x) . (\a b -> a*b), вы создаете функции следующих подписей:

(\x y z -> x) :: a -> b -> c -> a
(\a b -> a*b) :: Num a => a -> a -> a

Подпись (.) равна

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

Теперь давайте объединяем вещи для получения специализированной версии сигнатуры (.) для этих функций.

  • Сначала мы специализируем (b -> c) часть (.) подписи на a -> b -> c -> a:

    (b -> (z -> x -> b)) -> (a -> b) -> a -> (z -> x -> b)
    

    Получить? c становится (z -> x -> b).

  • Теперь давайте специализации (a -> b) части (.) подписи к a -> a -> a:

    ((a -> a) -> (z -> x -> (a -> a))) -> (a -> (a -> a)) -> a -> (z -> x -> (a -> a))
    

    b становится (a -> a).

  • Теперь отбросьте лишние фигурные скобки:

    ((a -> a) -> z -> x -> a -> a) -> (a -> a -> a) -> a -> z -> x -> a -> a
    
  • Теперь здесь сеанс от ghci, показывающий, как изменяется подпись, а затем применяю все аргументы к функции этой сигнатуры:

    > let f = undefined :: ((a -> a) -> z -> x -> a -> a) -> (a -> a -> a) -> a -> z -> x -> a -> a
    > :t f (\x y z -> x)
    f (\x y z -> x) :: (a -> a -> a) -> a -> z -> x -> a -> a
    > :t f (\x y z -> x) (\a b -> a*b)
    f (\x y z -> x) (\a b -> a*b) :: Num a => a -> z -> x -> a -> a
    > :t f (\x y z -> x) (\a b -> a*b) 2
    f (\x y z -> x) (\a b -> a*b) 2 :: Num a => z -> x -> a -> a
    > :t f (\x y z -> x) (\a b -> a*b) 2 3
    f (\x y z -> x) (\a b -> a*b) 2 3 :: Num a => x -> a -> a
    > :t f (\x y z -> x) (\a b -> a*b) 2 3 4
    f (\x y z -> x) (\a b -> a*b) 2 3 4 :: Num a => a -> a
    > :t f (\x y z -> x) (\a b -> a*b) 2 3 4 5
    f (\x y z -> x) (\a b -> a*b) 2 3 4 5 :: Num a => a    
    

Приведенное выше объясняет, как работает ( (\x y z -> x).(\a b -> a*b) ) 2 3 4 5.

Теперь, как ( (\x y z -> y).(\a b -> a*b) ) 2 3 4 5 переводит:

((a -> a) -> z -> x -> z) -> (a -> a -> a) -> a -> z -> x -> z

И вот результаты сеанса:

> let f = undefined :: ((a -> a) -> z -> x -> z) -> (a -> a -> a) -> a -> z -> x -> z
> :t f (\x y z -> x)
f (\x y z -> x) :: (a -> a -> a) -> a -> (a -> a) -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b)
f (\x y z -> x) (\a b -> a*b)
  :: Num a => a -> (a -> a) -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b) 2
f (\x y z -> x) (\a b -> a*b) 2 :: Num a => (a -> a) -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b) 2 3
f (\x y z -> x) (\a b -> a*b) 2 3
  :: (Num a, Num (a -> a)) => x -> a -> a

В последней строке объясняется ваше сообщение об ошибке. Очевидно, не может быть никакого экземпляра Num для a -> a.

Ответ 3

Поскольку вы понимаете, что все это функция с одним аргументом, пусть начнется с этой точки. Имейте в виду, что (\ xyz → x) действительно (\ x → (\ yz → x)), что в свою очередь действительно (\ x → (\ y → (\ z → x))), но остановитесь на первом шаге, чтобы сохранить шум в скобках.

(f . g) x = f (g x)

следовательно,

((\x -> (\y z -> x)) . (\a b -> a*b)) 2 =
(\x -> (\y z -> x)) ((\a -> (\b -> a*b)) 2) =
(\x -> (\y z -> x)) (\b -> 2*b) =
(\y z -> (\b -> 2*b))

Теперь запомните второй шаг и развернуть (\ y z → ...):

(\y z -> (\b -> 2*b)) 3 4 =
(\y -> (\z -> (\b -> 2*b))) 3 4 =
   -- \y -> ... given anything, returns a function \z -> ...
(\z -> (\b -> 2*b)) 4 =
   -- \z -> ... given anything, returns a function \b -> ...
(\b -> 2*b)

который, наконец, равен:

(\b -> 2*b) 5 = 2*5 = 10

История разворачивается по-другому, если первая функция возвращает y вместо x:

((\x -> (\y z -> y)) . (\a -> (\b -> a*b))) 2 =
(\x -> (\y z -> y)) ((\a -> (\b -> a*b)) 2) =
(\x -> (\y z -> y)) (\b -> 2*b) =
   -- \x -> ... given anything, returns a function \y z -> ...
(\y z -> y)

чтобы вы получили:

(\y -> (\z -> y)) 3 4 5 =
   -- \y -> ... given anything, returns a function \z -> ...
(\z -> 3) 4 5 =
   -- \z -> ... given anything, returns a constant 3
3 5  -- now still trying to apply 5 to 3

Он пытается рассматривать 3 как функцию, которая может принимать 5.

Ответ 4

Комбинатор . является функцией композиции. Рассмотрим его тип:

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

Таким образом, он принимает результат второй функции и передает ее первой функции.

В вашем примере важно учитывать, что мы можем рассматривать * как односложную функцию, результатом которой является функция:

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

Он принимает число и возвращает функцию, которая умножает свой аргумент на число. (Этот подход называется Currying). Оператор типа -> связывается вправо, поэтому скобки являются необязательными - это просто в наших умах, если мы рассматриваем (*) как функцию с двумя аргументами, возвращающую функцию числа или одного аргумента, возвращающую другую функцию.

Это помогает нам понять, что делает (*) . succ: он увеличивает свой аргумент (который должен быть Enum), а затем преобразует его в функцию, которая умножает свой аргумент на число (поэтому аргумент должен также быть Num). В результате получается

(*) . succ :: (Enum a, Num a) => a -> (a -> a)

Опять же, мы можем рассматривать его как функцию с одним аргументом или более удобную функцию с двумя аргументами: она увеличивает свой первый аргумент и умножает его на второй.

Ответ 5

В

( (\x y z -> x).(\a b -> a*b) ) 2 3 4 5
  • они сначала оценивают (\ a b → a * b) 2, что приводит к такой функции (\ b → 2 * b)
  • затем оцениваем (\ xyz → x) (\ b → 2 * b) 3 4, которые говорят, что взять функцию, взять 3 и взять 4 и вернуть функцию, что-то вроде этого: ((\ b → 2 * b) 3 4 → (\ b → 2 * b))
  • и остается только (\ b → 2 * b) 5, что приводит к результату 2 * 5 = 10

но в

( (\x y z -> y).(\a b -> a*b) ) 2 3 4 5

вторая оценка приведет к этому ((\ b → 2 * b) 3 4 → 3), оставшемуся 3 5, вызвав ошибку