【问题标题】:f# return a partial function from a functionf# 从函数返回部分函数
【发布时间】:2012-06-21 19:59:27
【问题描述】:

虽然将部分函数传递给另一个函数很简单,但如何从函数返回具有不同签名的部分函数?

这是我正在尝试的基本代码,随后进行了各种尝试以使其正常工作:

type InitData() =
    static member arrayIntAsc count = [|1..count|] 
    static member seqIntAsc count = {1..count}
    static member listIntAsc count = [1..count]
    (*more diverse signatures*)

module x =
    let getInitDataFun (initData:string) =
        match initData.ToLower() with
        | "arrayintasc" -> InitData.arrayIntAsc
        | "seqintasc" -> InitData.seqIntAsc
        | "listintasc" -> InitData.listIntAsc
        (*more diverse signatures*)
        | _ -> failwithf "InitData function %s not recognized" initData
  1. 尝试以各种方式强制使用通用返回签名,但 F# 3.0 总是将 getInitDataFun 返回签名强制为第一个匹配项的签名:

    let getInitDataFun (initData:string) : 'a -> 'b  = ...
    let getInitDataFun (initData:string) : _ -> _  = ...
    let getInitDataFun (initData:string) : int -> #(int seq)  = ...
    let getInitDataFun (initData:string) : int -> #('a seq)  = ...
    (*even if I could get (int -> #(int seq)) to work, I would like to return
      signatures not in this pattern too*)
    
  2. 尝试过的装箱/拆箱:

    | "arrayintasc" -> box InitData.arrayIntAsc
    

    这会编译,但拆箱尝试会引发运行时错误:

    未处理的异常:System.InvalidCastException:无法将 'RangeInt32@4819-2' 类型的对象转换为 'System.Collections.Generic.IEnumerable`1[System.Object]' 类型

  3. 尝试将部分函数作为引号返回,但遇到了类似的问题。如果我返回输入的引号,则返回不同的 Expr 签名有同样的问题。我可以返回无类型的引用,但是我必须在调用方知道返回的无类型表达式的签名。

  4. 考虑反射,但基本相同的问题是在调用时需要知道实际签名。

  5. 也尝试过以各种方式向上转换部分函数。

【问题讨论】:

  • 您能解释一下为什么要这样做吗?您希望来电者如何使用getInitDataFun

标签: f#


【解决方案1】:

我认为这就是你想要的

type InitData() = 
    static member arrayIntAsc count = [|1..count|]  
    static member seqIntAsc count = {1..count} 
    static member listIntAsc count = [1..count] 
    (*more diverse signatures*) 

let getInitDataFun (initData:string) : obj = 
    match initData.ToLower() with 
    | "arrayintasc" -> box InitData.arrayIntAsc 
    | "seqintasc" -> box InitData.seqIntAsc 
    | "listintasc" -> box InitData.listIntAsc 
    (*more diverse signatures*) 
    | _ -> failwithf "InitData function %s not recognized" initData 

let a = ((getInitDataFun "arrayintasc") :?> int->int[]) 20
printfn "%A" a

或者我不清楚你在问什么。

【讨论】:

  • 也许我不是很清楚,我不想知道调用方的签名。您的解决方案需要知道返回的签名才能进行向下转换。
  • @Jack : F# 是一种强静态类型语言——调用者需要知道它在调用什么。
  • @ildjam:是的。我认为我通过转发不同签名的函数而不是试图返回它们来解决正确解决我的问题的方法。我最初的意图和问题是命令式思维的残余。
【解决方案2】:

最好的办法是将静态成员更改为都返回相同的类型:

type InitData() =
    static member arrayIntAsc count = seq [|1..count|] 
    static member seqIntAsc count = {1..count}
    static member listIntAsc count = seq [1..count]

或用一个执行强制转换的函数包装它们:

let getInitDataFun (initData:string) =
    let asSeq f x = f x :> seq<_>
    match initData.ToLower() with
    | "arrayintasc" -> asSeq InitData.arrayIntAsc
    | "seqintasc" -> InitData.seqIntAsc
    | "listintasc" -> asSeq InitData.listIntAsc

可以让它通用:

let getInitDataFun<'T when 'T :> seq<int>> (initData:string) : (int -> 'T) =
    match initData.ToLower() with
    | "arrayintasc" -> (box >> unbox) InitData.arrayIntAsc
    | "seqintasc" -> (box >> unbox) InitData.seqIntAsc
    | "listintasc" -> (box >> unbox) InitData.listIntAsc

但如果预期返回类型错误,它会生成运行时异常:

let f = getInitDataFun "arrayintasc"
let x : int list = f 10 //BOOM!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-01
    • 1970-01-01
    • 2022-10-01
    • 1970-01-01
    相关资源
    最近更新 更多