Ответ 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
Я не понимаю составные функции с 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 имеет только один параметр, но мне это действительно не помогает:)
Разработайте так:
(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
Когда вы составляете (\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
.
Поскольку вы понимаете, что все это функция с одним аргументом, пусть начнется с этой точки. Имейте в виду, что (\ 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
.
Комбинатор .
является функцией композиции. Рассмотрим его тип:
(.) :: (b -> c) -> (a -> b) -> a -> c
Таким образом, он принимает результат второй функции и передает ее первой функции.
В вашем примере важно учитывать, что мы можем рассматривать *
как односложную функцию, результатом которой является функция:
(*) :: Num a => a -> (a -> a)
Он принимает число и возвращает функцию, которая умножает свой аргумент на число. (Этот подход называется Currying). Оператор типа ->
связывается вправо, поэтому скобки являются необязательными - это просто в наших умах, если мы рассматриваем (*)
как функцию с двумя аргументами, возвращающую функцию числа или одного аргумента, возвращающую другую функцию.
Это помогает нам понять, что делает (*) . succ
: он увеличивает свой аргумент (который должен быть Enum
), а затем преобразует его в функцию, которая умножает свой аргумент на число (поэтому аргумент должен также быть Num
). В результате получается
(*) . succ :: (Enum a, Num a) => a -> (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
вторая оценка приведет к этому ((\ b → 2 * b) 3 4 → 3), оставшемуся 3 5, вызвав ошибку