我应该使用什么数据结构来保存F#中的函数集合

时间:2015-05-04 20:11:38

标签: f#

值得一提的数据结构的一些功能: 这些功能不会都有相同的签名(我的主要问题) 2.数据结构不必是不可变的

我对所有主要的vanilla数据结构感到满意,我关心的是“泛化类型”是表示这类函数集合中的函数的最佳类型。特别是,他们的任何东西都比将它们装箱为obj / System.Object?

更好

当函数被“打包”到数据结构中时,我将知道它的签名。这可以以某种方式存储并链接到此函数,以便在需要时可以将函数“解包”为正确的形式。

我假设我可以创建函数签名类型并将它们存储在自己的集合中 - 也许这是一个不好的假设?换句话说:

let f1: float-> float
let f2: string->bool->float

typeof(f1)typeof(f2)是否可以存储在同一数据结构中。我认为既然它们都是Type类型,这应该是可能的

@ildjarn在回复下面的评论时:预期的签名可能是通常的嫌疑人的任何1-5输入参数组合,例如float,string,bool,date和结果参数在float上的变化。字符串|布尔。

考虑到上述情况,如何最好地存储这些功能,以便以后检索和应用(部分或全部)和/或与其他功能合成。

修改

我能想出的最简单的方法如下:

let f1 x = exp x
let t1 = f1.GetType()
let o1 = box f1 // alternate syntax
let f11 = o1 :?> t1 // ?? FAILS HERE!!!!!
f11 3.0

但是我不知道如何从对象转发回到拳击之前的函数类型。

2 个答案:

答案 0 :(得分:2)

为不同的sigs建立一个有区别的联盟(DU):

type Function<'a, 'b, 'c, 'd> =
| Unary of ('a -> 'd)
| Binary of ('a -> 'b -> 'd)
| Ternary of ('a -> 'b -> 'c -> 'd)

然后说出Function<'a, 'b, 'c, 'd> list来保存你的功能。这应该足够好,只要签名不是太多异类。因为您仍然需要拥有与DU中的案例一样多的不同类型的签名。

完整的示例可能如下所示:

open System

type Operator = 
| Parse of (string -> int)
| Unary of (int -> int)
| Binary of (int -> int -> int)
| Print of (int -> unit)

type Data =
| Int of int
| String of string

type StackContent =
| Data of Data
| Operator of Operator

let input = [
    Data (String "3")
    Operator (Parse Int32.Parse)
    Data (String "5")
    Operator (Parse Int32.Parse)
    Operator (Binary (+))
    Operator (Unary (~-))
    Operator (Print (printfn "%d"))]

let eval input =
    let rec eval = function
        | Data d :: inputTail, stack -> eval (inputTail, d::stack)
        | Operator (Parse parse) :: inputTail, String s :: stackTail -> eval (inputTail, Int (parse s) :: stackTail)
        | Operator (Binary (++)) :: inputTail, Int l :: Int r :: stackTail -> eval (inputTail, Int (l ++ r) :: stackTail)
        | Operator (Unary (!)) :: inputTail, Int i :: stackTail -> eval (inputTail, Int !i :: stackTail)
        | Operator (Print print) :: inputTail, Int i :: stackTail ->
            print i
            eval (inputTail, stackTail)
        | [], [] -> ()
        | input, stack -> failwithf "the following thing is not properly typed\nInput: %A\Stack: %A" input stack
    eval (input,[])

eval input

答案 1 :(得分:1)

这取决于您执行此操作的上下文,但您需要以某种方式包装函数和参数。您可以将它们打包并使用obj值(然后使用反射),您可以将它们包装在有区别的联合中,您可能会做其他事情。

歧视的工会方法可能是最简单的方法。您可以为您支持的不同类型的值设置DU:

type Value =
  | Int of int
  | String of string

然后一个函数获取一个值列表并生成一个值(你可以选择它,因为如果它得到不正确的参数,函数可能会失败):

type Function = Value list -> Value option

要定义函数集合,可以创建列表。每个函数都会在输入上进行模式匹配,以确保它获得预期的值:

let functions = 
  [ ( function 
      | [ Int n; String t ] -> 
          Some(String(sprintf "The %s is %d" t n))
      | _ -> None) ]

然后你可以创建一个参数列表并调用函数:

let arguments = [ Int 42; String "Answer" ]    
functions.[0] arguments

这实际上只是众多选项中的一种,但它是最简单的选择之一。缺点是您需要显式解包参数并将结果包装在Value中 - 但您可能稍后使用某种反射或类型转换来自动化它。