Сделай мне немного карри

20

Имея функцию f, которая принимает аргументы x 1 , x 2 ,…, x n

                                               - т.е.  f: X 1 × X 2 ×… × X n → Y

- curry переопределяет f как функцию, принимающую один аргумент a 1, который отображается на еще одну функцию. Этот метод полезен для частичного применения, например, с помощью powфункции карри, которую мы могли бы написать exp = pow(e).

пример

Предполагая, что у нас есть следующая функция f, принимающая три аргумента ( f: X 1 × X 2 × X 3 → Y ):

def f(a,b,c):
  return a + b * c

Карринг этой функции оставляет нас с f_curry: X 1 → (X 2 → (X 3 → Y)) , если бы мы теперь вызывали эту функцию дважды, f_curry(1)(2)мы бы получили функцию ( h), эквивалентную следующему:

def h(c):
   return 1 + 2 * c

Функция карри fможет быть написана так (Python 3):

def f_curry(a):
  def g_curry(b):
    def h(c):
      return a + b * c
    return h
  return g_curry

Попробуйте онлайн!

Вызов

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

  • Ввод будет функцией черного ящика, которая принимает как минимум 2 аргумента
  • Функция ввода всегда будет иметь фиксированное количество аргументов (в отличие printfили аналогично, обратите внимание: вам нужно поддерживать функции с любым количеством аргументов ≥2)
  • Если в вашем языке по умолчанию используются функции карри (например, Haskell), вы можете ожидать, что функция ввода будет определена для N- кортежей, а не для «функции более высокого порядка».
  • Вы можете принять количество аргументов в качестве ввода
  • Выход будет эквивалентным карри ввода *
  • Вы можете предположить, что функция вывода будет только когда-либо:
    • вызывается с меньшим или равным количеству аргументов, которые принимает входная функция
    • вызывается с аргументами правильного типа

* Это будет означать для входных данных fс Nаргументами и выходных данных, hчто для всех допустимых аргументов, a1,…,aNон содержит это f(a1,a2,…,aN) == h(a1)(a2)…(aN).

ბიმო
источник
Относящиеся .
ბიმო
так что вход есть, def f(a,b,c): return a + b * cа выход есть def f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry?
DanielIndie
@DanielIndie: Если вы берете этот пример, то вход будет f(который где-то определен), а результат должен быть чем-то эквивалентным f_curry. Или вход будет, lambda a,b,c: a+b*cа выход - функцией, эквивалентной f_curry.
მოიმო
Это трудно сделать в большинстве статически типизированных языков ... Я думаю, вам нужны функции для этого.
Paŭlo Ebermann
@ PaŭloEbermann: правда, некоторые языки не смогут решить эту задачу (обратите внимание на тег функциональное программирование ). Однако некоторые статически типизированные языки могут быть в состоянии использовать указатели на функции, которые будут допустимым вводом / выводом, поэтому я в основном разрешил принимать количество аргументов в качестве дополнительного ввода.
ბიმო

Ответы:

11

JavaScript (ES6), 35 байт

f=g=>g.length<2?g:a=>f(g.bind(f,a))
Нил
источник
9

Идрис , 204 байта

import Data.HVect
C:(a:Vect n Type)->(HVect a->Type)->Type
C[]T=T[]
C(h::t)T=(x:h)->C t(T .(x::))
c:{a:Vect n Type}->{T:HVect a->Type}->((b:HVect a)->T b)->C a T
c{a=[]}f=f[]
c{a=h::t}f=\v=>c(\u=>f(v::u))

Попробуйте онлайн!

Звучит как работа для зависимых типов! Что же, может быть.


C - это функция типа карри. Для данного вектора типов a = [t 1 , t 2 ,… t n ] и функции типа T: HVect a → Type он возвращает новый тип:

           (x 1  : t 1 ) → (x 2  : t 2 ) →… → (T [x 1 , x 2 ,… x n ])

Здесь HVect - гетерогенный векторный тип из Idris Prelude - тип n- кортежей, элементы которых имеют n разных типов.

с представляет собой функцию , которая принимает и Т , как неявных аргументов, а затем преобразует uncurried функцию типа ((б: HVect а) → T B) в каррированной одного типа C A T .f

( C просто описывает то, что мы хотим сделать; c на самом деле делает это. Но мы не можем обойтись без определения C , поскольку Idris требует, чтобы каждое определение верхнего уровня имело сигнатуру типа.)


Ссылка TIO дает пример использования. Если мы определим функцию для трех кортежей (Nat, Nat, String) следующим образом:

uncurried : HVect [Nat, Nat, String] -> String
uncurried [a, b, c] = show (a*a + b*b) ++ c

затем uncurried [3, 4, "th"]дает тот же результат, что и c uncurried 3 4 "th". Идрис выводит аргументы a=[Nat, Nat, String]и T=const Stringдля нас, я считаю.

Я основал этот код на этой сути Timjb.

Линн
источник
На мой взгляд, кортежи в Haskell и Idris должны быть HVectпо умолчанию HVect- это, по сути, кортеж, который вы можете отключить.
Esolanging Fruit
5

R , 96 байт

y=function(f,n=length(formals(f)),p=list())function(x,u=c(p,x))`if`(n<2,do.call(f,u),y(f,n-1,u))

Попробуйте онлайн!


Предыдущая версия (97 байт)

-1 байт благодаря @JayCE

digEmAll
источник
Я не вижу, как это существенно сократить. Вы можете убрать три байта, избавившись от скобок и пробела в конце первой строки. И еще два из-за соглашения здесь о не включении имени функции в число байтов. TIO
НГМЫ
@ngm Имя функции должно быть включено, когда оно рекурсивное.
Орджан Йохансен
@ngm: я поместил оператор if внутри подфункции, сохранив десятую часть байтов :)
digEmAll
3

Python 2 , 60 байт

c=lambda f,n,l=[]:lambda a:n-1and c(f,n-1,l+[a])or f(*l+[a])

Попробуйте онлайн!

Нижний колонтитул - это тестер, который использует STDIN для каждой строки следующим образом:

  1. Сама функция
  2. Количество аргументов функции, ≥2
  3. Список аргументов ( [a,b,...])

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

Аналогичная 55-байтовая версия была любезно предоставлена ovs :

c=lambda f,n,*l:lambda a:n-1and c(f,n-1,*l,a)or f(*l,a)

Попробуйте онлайн!

Эрик Outgolfer
источник
2

Цветная капуста , 84 байта

(:= c(\($f$n(@a))(if$n(\($a)(call c(cat(list$f(-$n 1))@a(list$a))))else(call$f@a))))

Попробуйте онлайн!

ASCII-только
источник
1
Ммм, цветная капуста карри. Вкусные. ^ _ ^
DLosc
@DLosc не хватает ответов на этот вызов в языках с именами, связанными с едой: P (хотя я думаю, что большинство из них на самом деле не имеют функций)
только ASCII
1

Атташе , 5 байт

Curry

Попробуйте онлайн!

Простой встроенный, во многом неинтересный. Но вот версия с нуля:

Атташе, 35 байт

{If[#__2<#_,Fold[`&:,$'__],_@@__2]}

Объяснение:

{If[#__2<#_,Fold[`&:,$'__],_@@__2]}
{                                 }    lambda
 If[       ,              ,      ]     if
    #__2                                 the number of parameters after the first
        <#_                              is less than the arity of the first
            Fold[   ,    ]             then fold:
                 `&:                     right-function bonding
                     $                     over this function
                      '__                  paired with the rest of the parameters
                          ,            otherwise:
                           _@@           call the first parameter
                              __2        with the rest of them
Конор О'Брайен
источник
1

Java 8, 46 + 318 = 364 байта

Это лямбда карри (хах), которая берет функцию и количество аргументов и возвращает функцию карри.

import java.lang.reflect.*;import java.util.*;

f->p->new java.util.function.Function(){Object F=f;Method m=F.getClass().getDeclaredMethods()[0];int P=p;List a=new Stack();public Object apply(Object r){a.add(r);try{return a.size()<P?this:m.invoke(F,a.toArray());}catch(Throwable t){t=t.getCause();if(t instanceof Error)throw(Error)t;else throw(RuntimeException)t;}}}

Попробуйте онлайн

Тип представления

Функция ввода

Функция input - это объект с единственным методом (исключая унаследованные методы), представляющим функцию. Обратите внимание, что стандартный функциональный интерфейс не может использоваться в качестве типа ввода, поскольку должны поддерживаться функции (например, 3 параметра). Также обратите внимание, что java.util.function.Functionможет быть передано лямбда-выражение, приведенное к стандартному типу (единственный метод - apply).

Проверенные исключения могут быть объявлены во входной функции, но они не могут быть выброшены (то есть они не будут переданы вызывающей стороне выходной функции). Предполагается, что это приемлемо, потому что функциональные интерфейсы Java не разрешают проверенные исключения (и их распространение не позволит отправке возвращать a Function). Исключения времени выполнения (присваиваемые RuntimeExceptionили Error) распространяются.

Функция выхода

Результатом представления является java.util.function.Function<Object, Object>. Я подумал о возврате равнины Objectс applyметодом (как во входных данных), но тогда потребовалось бы отражение, чтобы вызвать результат, который казался достаточно неудобным, чтобы его можно было запретить - в частности, вызов всего пути вниз был бы невозможен за один раз. выражение.

использование

Поскольку отправка возвращает функцию из Objectв Object, выходные данные могут быть вызваны напрямую (с помощью apply), но последующие промежуточные возвращаемые значения должны быть преобразованы в соответствующий тип (например java.util.function.Function<Object, Object>) перед вызовом. Консультируйтесь с TIO для некоторых примеров использования.

Обратите внимание, что в Java функции (то есть методы) не являются объектами первого класса. Таким образом, синтаксис, используемый в выходном маркере описания вызова, не имеет смысла в Java. Скорее, чем f(a1, a2, a3)мы f.apply(a1, a2, a3), и, скорее, чем f(a1)(a2)(a3)мы имеем f.apply(a1).apply(a2).apply(a3).

Ограничения

Когда применяется промежуточный результат (добавлен аргумент), результат фактически является видоизмененной копией исходного результата. Например, в этом фрагменте:

Function<Object, Function<Integer, Function>> submission = ...;
Function c = submission.apply((IntBinaryOperator) (a, b) -> a + b).apply(2);
Function c2 = (Function) c.apply(2);
System.out.println(c2.apply(2));
System.out.println(c2.apply(3));

строка 4 будет напечатана 4, но строка 5 потерпит неудачу, потому что к тому времени c2уже содержит аргументы 2и 2(обратите внимание, что c2 == c). Это нарушает дух карри, но отвечает конкретным требованиям, указанным в вызове.

Ungolfed

См. TIO для разглаженной копии.

Jakob
источник