在部分应用中使用序列

时间:2017-01-05 16:10:05

标签: f#

我有一个值序列,我想部分地应用于函数:

let f a b c d e= a+b+c+d+e

let items = [1,2,3,4,5]

let result = applyPartially f items

Assert.Equal(15, result)

我正在寻找applyPartially函数。我试过写这样的递归函数:

let rec applyPartially f items =
| [] -> f
| [x] -> f x
| head :: tail -> applyPartially (f head) tail

我遇到的问题是f类型在我的迭代开始'a->'b->'c->'d->'e,并且它应该消耗的每个循环订单。

'a->'b->'c->'d->'e 
'b->'c->'d->'e 
'c->'d->'e 
'd->'e

这意味着我能想到的下界面将是'd->'e。我怎么能隐藏我的函数的复杂性,以便在递归函数中只显示'd->'e?

2 个答案:

答案 0 :(得分:5)

F#类型系统没有以你建议的方式处理普通函数的好方法 - 要做到这一点,你需要确保列表的长度与参数的数量相匹配该功能,这是普通列表和功能无法实现的。

但是,您可以使用区分联合来很好地建模。您可以定义部分函数,​​该函数已完成或需要多一个输入:

type PartialFunction<'T, 'R> = 
  | Completed of 'R
  | NeedsMore of ('T -> PartialFunction<'T, 'R>)

你的函数f现在可以用PartialFunction<int, int>作为let f = NeedsMore(fun a -> NeedsMore(fun b -> NeedsMore(fun c -> NeedsMore(fun d -> NeedsMore(fun e -> Completed(a+b+c+d+e)))))) 来编写,它继续输入5个输入,然后返回结果:

applyPartially

现在你可以通过解构参数列表并逐个将它们应用到partial函数来实现let rec applyPartially f items = match f, items with | Completed r, _ -> r | NeedsMore f, head::tail -> applyPartially (f head) tail | NeedsMore _, _ -> failwith "Insufficient number of arguments" ,直到你得到结果:

applyPartially f [1;2;3;4;5]

以下现在按预期返回15:

dbq

答案 1 :(得分:3)

免责声明:请不要使用此功能。这只是简单evil

let apply f v =
    let args = v |> Seq.toArray    
    f.GetType().GetMethods()
    |> Array.tryFind (fun m -> m.Name = "Invoke" && Array.length (m.GetParameters()) = Array.length args)
    |> function None -> failwith "Not enough args" | Some(m) -> m.Invoke(f, args)

就像你期望的那样:

let f a b c d e= a+b+c+d+e
apply f [1; 2; 3; 4; 5] //15