如何链接二进制函数列表

时间:2016-03-31 09:06:16

标签: r function functional-programming

我对以下构造感兴趣:

假设我有一个n二元函数列表和一个n+1参数向量。为了论证,我们可以使用

flist = c(`+`,`-`)
args = 1:3

我想要做的是创建以下函数调用f2(f1(x1,x2),x3),即在此示例中

`-`(`+`(1,2),3)

其中返回值是累积结果的向量

[1] 3 0

我有一个表格

的解决方案
f = function(x,op,res = NULL){
  if(is.null(res)){
    res = op[[1]](x[1],x[2])
    x = x[-1]
  } else{
    res = c(res,op[[1]](res[length(res)],x[1]))
  }
  if(length(op) == 1) res
  else f(x[-1],op[-1],res)
}

这样就给出了正确的答案

f(x,flist)
[1] 3 0

但它并不觉得特别喜欢或优雅。有一个更好的方法吗。我怀疑我的实施也不是最有效的,所以任何更高效的东西也会引起人们的兴趣。

有人有任何想法吗?

或者,如果放宽了累积答案的要求,即只返回最终答案0,那么是否有一个很好的R方法可以做到这一点?我知道我可以修改我的f来处理这个替代方案,但如果有办法做到这一点,我很乐意听到这两种选择。

编辑:

评论建议for循环实施,以便我们可以

falt = function(x,op){
  res = numeric(length(op))
  res[1] = op[[1]](x[1],x[2])
  for(i in 2:length(res)) res[i] = op[[i]](res[i-1],x[i+1])
  res
}

效率更高。但我仍然觉得必须有一个更简洁的方法来做到这一点。

1 个答案:

答案 0 :(得分:2)

如果您的功能已经是咖喱形式,那就更容易了

comp <- function (f) function (g) function (x) f(g(x))

comp2 <- comp (comp) (comp) # if this is confusing, details later

add <- function (x) function (y) y + x
mult <- function (x) function (y) y * x

comp2 (mult) (add) (3) (4) (5)
# 5 * (4 + 3)
# 5 * 7
# 35

因为所有内容都是咖喱,所以您可以根据需要应用任意数量的参数,然后再应用其余的

compute <- comp2 (mult) (add)
compute (5) (6) (7)
# 7 * (6 + 5)
# 7 * 30
# 210

如果你有二进制函数列表,你可以使用左折(或#34; reduce&#34;)来创建一个完整的序列

identity <- function(x) x
comp <- function (f) function (g) function (x) f(g(x))
comp2 <- comp (comp) (comp)
uncurry <- function (f) function (x,y) f(x)(y)
reduce <- function(f) function(y) function (xs) Reduce(uncurry(f), xs, y)
comp2All <- reduce (comp2) (identity)

# some binary functions to use in our sequence
sub <- function (x) function (y) y - x
add <- function (x) function (y) y + x
mult <- function (x) function (y) y * x

# create a sequence of N binary functions
compute <- comp2All (list(mult, sub, mult, add))

# apply the computation to N+1 args
compute (3) (4) (5) (100) (0.2)
# 0.2 * (100 - (5 * (3 + 4))
# 0.2 * (100 - (5 * 7))
# 0.2 * (100 - 35)
# 0.2 * 65
# => 13

所以你可能不喜欢一次只应用一个参数...

# this kind sucks, right?
compute (3) (4) (5) (6) (7)

我们可以通过创建一个将curried函数应用于列表或参数

的函数来解决这个问题
capply <- reduce (identity)
capply (compute) (3:7)
# 7 * (6 - (5 * (4 + 3)))
# 7 * (6 - (5 * 7))
# 7 * (6 - 35)
# 7 * -29
# => -203

如果你的二进制函数还没有计算:

您可以使用curry2

轻松地理解二元函数
curry2 <- function(f) function(x) function(y) f(x,y)
curry2 (`+`) (3) (4)
# => 7

如果您有一个尚未加入咖喱的二进制函数的完整列表,则可以使用map

转换整个列表
map <- function (f) function (xs) Map(f,xs)
compute <- comp2All (map (curry2) (list (`*`, `+`, `*`, `+`)))
compute (3) (4) (5) (6) (7)
# 7 * (6 + (5 * (3 + 4)))
# 7 * (6 + (5 * 7))
# 7 * (6 + 35)
# 7 * 41
# => 287

comp vs comp2

因为您想要创建一系列二进制函数,所以我使用了

comp2All <- reduce (comp2) (identity)

如果你想要一系列一元函数,你可以使用

compAll <- reduce (comp) (identity)

什么是comp2?

comp2定义可能看起来令人费解,但不要让它混淆你

comp2 <- comp (comp) (comp)

如果我们要扩展这一点,我们首先要看

comp2 <- function (x) comp(comp(x))

进一步扩展(这是一项有趣的练习),你应该看到

comp2 <- function (f) function (g) function (x) function (y) f(g(x)(y))

可以很容易理解为一元函数的组成, f ,具有二元函数, g