Карри в Haskell с 2+ аргументами - PullRequest
3 голосов
/ 29 сентября 2019

Я начинаю изучать Haskell, поэтому мне также нужно понимать карри (это первый раз, когда я тоже вижу эту технику).Я думаю, я понимаю, как это работает в некоторых случаях, когда очистка только «исключает» один из параметров.Как в следующем примере, где я пытаюсь вычислить произведение 4 чисел.Это незакрашенная функция:

prod :: Integer->Integer->Integer->Integer->Integer
prod x y z t = x * y * z * t

Это карри:

prod' :: Integer->Integer->Integer->Integer->Integer
prod' x y z = (*) (x*y*z)

Но я не понимаю, как я могу продолжить эту динамику и, например, выполнить ту же функцию столько два аргумента и так далее:

prod'' :: Integer->Integer->Integer->Integer->Integer
prod'' x y =

Ответы [ 2 ]

5 голосов
/ 29 сентября 2019

Это необработанная функция:

prod :: Integer -> Integer -> Integer -> Integer -> Integer
prod x y z t = x * y * z * t

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

prod :: Integer -> (Integer -> (Integer -> (Integer -> Integer)))

Таким образом, Haskell создаст функцию, которая выглядит следующим образом:

prod :: Integer -> (Integer -> (Integer -> (Integer -> Integer)))
prod = \x -> (\y -> (\z -> (\t -> x * y * z * t)))

Действительно, мы можем, например, сгенерировать такую ​​функцию:

prod2 = prod 2

Это будет иметь тип:

prod2 :: Integer -> (Integer -> (Integer -> Integer))
prod2 = prod 2

, и мы можем продолжить с:

prod2_4 :: Integer -> (Integer -> Integer)
prod2_4 = prod2 4

и в конечном итоге:

prod2_4_6 :: Integer -> Integer
prod2_4_6 = prod2_4 6

РЕДАКТИРОВАТЬ

Функция prod' с:

prod'' x y = (*) ((*) (x*y))

Поскольку это означает, что вы умножаете (*) (x*y) на следующий параметр.Но (*) (x*y) это функция.Вы можете только умножить числа.Строго говоря, вы можете сделать номера функций.Но компилятор Haskell, таким образом, жалуется, что:

Prelude> prod'' x y = (*) ((*) (x*y))

<interactive>:1:1: error:
    • Non type-variable argument in the constraint: Num (a -> a)
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        prod'' :: forall a.
                  (Num (a -> a), Num a) =>
                  a -> a -> (a -> a) -> a -> a

Таким образом, он говорит, что вы здесь стремитесь выполнить операцию с функцией a -> a в качестве первого операнда, но эта функция не является экземпляром Num Тип класса.

4 голосов
/ 29 сентября 2019

То, что у вас есть, это

prod x y z t  =    x * y * z * t
              =   (x * y * z) * t
              =  (*) (x * y * z) t

Следовательно, на Это сокращение (где мы заменяем foo x = bar x на foo = bar)

prod x y z    =  (*) (x * y * z)
              =  (*) ( (x * y) * z )
              =  (*) ( (*) (x * y) z )
              =  ((*) . (*) (x * y)) z 

, так чтоЭто снова сокращение,

prod x y      =   (*) . (*) (x * y)

Здесь (.) - оператор композиции функции, определяемый как

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

То, о чем вы спрашиваете, известно как стиль без точек.«Точка без» означает «без явного упоминания [подразумеваемых] аргументов» (здесь «точка» - это жаргон математического выражения «аргумент»).

«Карринг» - это ортогональный вопрос, хотя Хаскель - карри language облегчает написание таких определений - и частичных применений, показанных в ответе Виллема.«Curry» означает, что функции принимают свои аргументы по одному, поэтому легко частично применить функцию к значению.

Мы можем продолжить процесс извлечения последнего аргумента из , поэтомуэто может быть устранено путем сокращения eta в дальнейшем.Но обычно это быстро приводит к все более запутанному коду, например prod = ((((*) .) . (*)) .) . (*).

Это потому, что письменный код - это одномерное кодирование структуры двумерного (или даже многомерного) вычислительного графа, присущей только ему,

  prod =           
                  /
                 *
                / \
               *   
              / \
         <-- *   
              \

Вы можете поэкспериментировать с ним здесь .Например, если бы (*) было ассоциативно правым, мы получили бы еще более запутанный код

\x y z t -> x * (y * (z * t))
==
(. ((. (*)) . (.) . (*))) . (.) . (.) . (*)

, представляющий столь же отчетливо выглядящую, слегка переставленную структуру графика

              / 
         <-- *   
              \ /
               *
                \ /
                 *
                  \
...