我有一个值序列,我想部分地应用于函数:
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?
答案 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