让我来点咖喱


20

有一个函数f带有参数x 1x 2,…,x n

                                               –即  f:X 1 ×X 2 ×…×X n →Y

curryingf重新定义为带有单个参数a 1的函数,该参数映射到另一个函数。这项技术对于部分应用很有用,例如具有pow我们可以编写的curried 函数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

curried函数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
ბიმო

在大多数静态类型的语言中很难做到这一点……我想您需要为此使用类型函数。
圣保罗Ebermann

@PaŭloEbermann:的确,某些语言将无法解决此任务(请注意标记functional-programming)。但是,某些静态类型的语言可能能够使用有效的I / O函数指针,这主要是我允许将参数数量作为附加输入的原因。
ბიმო

Answers:


11

JavaScript(ES6),35个字节

f=g=>g.length<2?g:a=>f(g.bind(f,a))

9

Idris,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异质向量类型从伊德里斯前奏-的类型Ñ元组,其元素的Ñ不同的类型。

Ç是一个函数,它Ť为隐式参数,然后转换的uncurried功能f型的((B:HVect一个)→T B)咖喱类型的一个℃的Ť

C只是描述了我们希望做的事;c确实做到了。但是我们不能不定义C,因为Idris要求每个顶级定义都必须具有类型签名。)


TIO链接提供了一个使用示例。如果我们按以下方式在3元组(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和伊德里斯元组实际上应该是HVect由默认- HVect本质上是一个元组,你可以uncons。
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


我不知道如何从根本上缩短它。您可以通过消除第一行末尾的花括号和空格来打掉三个字节。还有两个,这是由于这里的约定,即在字节数中不包括函数的名称。TIO
ngm

@ngm函数名称必须是递归的。
与Orjan约翰森

@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,...]

请注意,虽然在测试器中将参数列表作为输入给出,但实际上,咖喱等效项会放在该列表的前面,并且通过函数调用来减少该列表。

ovs提供了一个类似的55字节版本:

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

在线尝试!


2

花椰菜,84字节

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

在线尝试!


1
嗯,花椰菜咖喱。美味的。^ _ ^
DLosc

@DLosc在与食物相关的名称的语言中,没有足够的答案来应对这一挑战:P(尽管我想其中大多数实际上没有功能)
ASCII码,仅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字节

这是一个咖喱(hah)lambda,接受一个函数和一个参数计数并返回该咖喱函数。

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;}}}

在线试用

投稿类型

输入功能

函数输入是一个具有单个表示该函数的方法(不包括继承的方法)的对象。请注意,标准功能接口不能用作输入类型,因为必须支持(例如)3个参数的功能。另外请注意,java.util.function.Function可以传递转换为类似标准类型的lambda表达式(单个方法是apply)。

可以在输入函数中声明已检查的异常,但是可能不会引发它们(即,它们不会传播到输出函数的调用方)。假定这是可以接受的,因为Java的功能接口不允许检查异常(并且传播它们会阻止提交返回Function)。传播运行时异常(可分配给RuntimeExceptionError)。

输出功能

提交的输出为java.util.function.Function<Object, Object>。我考虑过Object使用apply方法返回平原(例如在输入中),但是随后需要反射来调用结果,这似乎很不方便,以至于是不允许的-特别是,无法一次向下调用所有方法表达。

用法

由于提交返回的函数是从ObjectObject,因此可以直接调用输出(使用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为止已经保存了参数22(也请注意c2 == c)。这违反了欺骗的精神,但符合挑战中所述的特定要求。

不打高尔夫球

请参阅TIO以获取完整版本。



0

APL(Dyalog Classic)58 57字节

r←(a f g)x
:If a[1]=≢a
rg 1a,x
:Else
r←(a,x)f g
:End

在线尝试!

调用语法(使用curried函数gx1通过传递参数x3和传递参数数量n):

((n x1 f g) x2) x3

需要 ⎕IO←1

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.