【问题标题】:How to curry a function at a given argument position如何在给定的参数位置柯里化函数
【发布时间】:2019-07-10 16:22:09
【问题描述】:

是否可以在用户定义的第 n 个位置对函数进行柯里化? Functool 的偏函数可以处理多个参数的部分求值,

from functools import partial

def f(x,y,z):
    return [x,y,z]

然后

partial(f,1)(2,3)

[1,2,3]

partial(f,1,2)(3)

[1,2,3]

是否有可能在给定的参数位置柯里化?

curry_at(f,n)

这样

curry_at(f,2)(1,3)(2)

给予

[1,2,3]

在哪里,我认为要保持与可能的 *arg 和 **kwarg 一致,n 应该不大于常规参数的数量?

有没有枚举参数的方法,将前缀和后缀参数分隔到第n个槽?

【问题讨论】:

标签: python functional-programming currying


【解决方案1】:

建议构建这样的函数并不常见,但在这种情况下,我认为最好有一些重复的静态定义,而不是依赖于复杂行为难以验证的动态函数 -

def apply_at_1 (f, *a, **kw):
  return lambda x, *a2, **kw2: \
    f(x, *a, *a2, **kw, **kw2)

def apply_at_2 (f, *a, **kw):
  return lambda x, y, *a2, **kw2: \
    f(x, y, *a, *a2, **kw, **kw2)

def apply_at_3 (f, *a, **kw):
  return lambda x, y, z, *a2, **kw2: \
    f(x, y, z, *a, *a2, **kw, **kw2)

def dummy (a, b, c, d, *more):
  print (a, b, c, d, *more)

apply_at_1(dummy, 2, 3, 4)(1, 5, 6, 7, 8)
# 1 2 3 4 5 6 7 8

apply_at_2(dummy, 3, 4, 5)(1, 2, 6, 7, 8)
# 1 2 3 4 5 6 7 8

apply_at_3(dummy, 4, 5, 6)(1, 2, 3, 7, 8)
# 1 2 3 4 5 6 7 8

当然,动态函数允许您在 any 位置应用参数,但如果您需要在位置 4(第 5 个参数)应用参数,也许是时候考虑重构了。在这种情况下,我认为最好设置一个合理的限制,然后约束自己。

其他函数式语言出于打字原因做类似的事情。考虑 Haskell 的 liftA2liftA3liftA4 等。或者 Elm 的 List.mapList.map2List.map3List.map4、...

有时接吻更容易。


这是支持通配符的partial 版本,__ -

def __ ():
  return None;

def partial (f, *a):
  def combine (a, b):
    if not a:
      return b
    elif a[0] is __:
      return (b[0], *combine(a[1:], b[1:]))
    else:
      return (a[0], *combine(a[1:], b))
  return lambda *b: f(*combine(a,b))

def dummy (a, b, c, d, *more):
  print (a, b, c, d, *more)

partial(dummy, 1, __, __, 4)(2, 3, 5, 6, 7, 8) 
# 1 2 3 4 5 6 7 8

partial(dummy, __, __, 3, __, __, 6, 7, 8)(1, 2, 4, 5)
# 1 2 3 4 5 6 7 8

【讨论】:

  • 我一直使用另一种功能性、可选类型语言的“动态”版本(此外,它允许在所需位置使用直接位置通配符运算符来指示它)。我想不出为什么你会认为这更好。
  • PS,只要KISS是DRY
  • @alancalvitti,因为它的复杂性为零,几乎不需要测试。它基本上是一个语法重写器。由于需要更多的中间值和容易出错的逻辑,因此任何动态实现都会变慢。我在帖子中指出了“重复”,并且已经说过以这种方式编写函数并不常见。但是在这种特定情况下,我认为这种方法有很多优点。并且通过其他流行语言使用类似技术的证据,我认为它增加了答案的可信度。如果/当你发现/写了一个在 Python 中使用的动态解决方案,请分享。
  • 我不知道 Python 能做什么,它的功能远不如我使用了 20 多年的语言(参见 Paul Graham 的 Blub paradox)。但我可以告诉你我每天使用的动态版本,没有问题或严重的减速 - 正如你指出的那样,解释器将其处理为简单的术语重写。
  • “我每天使用的动态版本...” - 使用什么语言?口齿不清?这不是一个公平的比较。你问的是如何在 Python 中做到这一点,而语言功能完全不同。
【解决方案2】:

我尝试了使用索引和 for 循环的工作方法,希望对您有所帮助:

from functools import partial

def curry_at(funtion,parameter,index,*args):
  h = funtion
  index-=1
  ls = list(args)
  ls.insert(index,parameter)
  for arg in ls:
      h = partial(h,arg)
  return h()

a = curry_at(f,'a',2,'c','d')

b = curry_at(f,2,2,1,3)

一个

['c', 'a', 'd']

b

[1,2,3]

【讨论】:

  • @alancalvitti 因为你误解了签名,首先是索引然后是参数,我会进行调整。看
  • @alancalvitti 我更新了答案,你现在可以试试吗?
  • 几个问题 - 可能可以修复。首先,curry_at 应该返回一个函数,以便可以将参数应用于该函数。此外,例如curry_at(f,'x',5,'a','c','d') 在错误的位置错误地插入了“x”。或许更难,如何处理**kwarg
  • 1) 简单地返回一个函数,你返回 h 而不是 h(),当然还有 curry_at(f,'x', 5, 'a','b', ' c') 会起到额外的作用,在使用这个特定的 curry_at 之前你必须知道函数的参数数量
  • 在调用curry_at 时不会传递参数'x'。这将被传递给curry_at 返回的函数
猜你喜欢
  • 2018-01-03
  • 2016-01-01
  • 1970-01-01
  • 2021-02-07
  • 2012-10-06
  • 1970-01-01
  • 1970-01-01
  • 2014-06-11
  • 1970-01-01
相关资源
最近更新 更多