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

时间:2022-09-11 12:07:01

Some features of the data structure worth mentioning: 1. The functions will NOT all have the same signatures (my main issue) 2. The data structure does not have to be immutable

值得一提的数据结构的一些特性:1。函数不会都具有相同的签名(我的主要问题)2。数据结构不必是不可变的

I'm comfortable with all the main vanilla data structures, my concern is around what "generalized type" is the best umbrella type to represent a function in such a collection of functions. In particular, is their anything better than just boxing them as obj / System.Object?

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

When the function is being "packed" into the data structure I will know it's signature. This can be stored in some way and linked to this function such that it can be used to "unpack" the function into the correct form when it is needed.

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

I am assuming that I can create function signature types and store them in a collection of their own - perhaps this is a bad assumption though? In other words:

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

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

Can the typeof(f1) and typeof(f2) be stored in the same data structure. I would think that since they are both of type Type this should be possible

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

In reply to the comment below by @ildjarn: The expected signatures could be any 1-5 input parameter combinations of the usual suspects like float, string, bool, date and with the result parameter varying equally over float | string | bool.

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

Taking the above into account, how best can the functions be stored for later retrieval and application (partial or full) and or composition with other functions.

考虑到上述因素,如何最好地存储功能以供以后检索和应用(部分或全部)和/或与其他功能组合。

EDIT

The simplest approach I can come up with is the following:

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

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

But I don't know how to downcast from the object back to the function type I had prior to boxing.

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

2 个解决方案

#1


Build yourself a discriminated union (DU) for the different sigs:

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

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

then make say Function<'a, 'b, 'c, 'd> list to hold your functions. This should work well enough, so long as the signatures are not too heterogeneous. Because you still require to have at most as many different kinds of signatures as you have cases in your DU.

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

A fully working example might look like this:

一个完整的工作示例可能如下所示:

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

#2


This depends on the context in which you are doing this, but you'll need to wrap the functions and the arguments in some way. You could box them and work with obj values (and then use reflection), you could wrap them in discriminated unions and you could likely do other things.

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

The discriminated union approach is probably the easiest. You could have a DU for different kinds of values that you support:

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

type Value =
  | Int of int
  | String of string

A function then takes a list of values and produces a value (you can make it option, because the function might fail if it gets incorrect arguments):

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

type Function = Value list -> Value option

To define your collection of functions, you can create a list. Each function will pattern match on the input to make sure it is getting the expected values:

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

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

Then you can create a list of arguments and call the function:

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

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

This is really just one of multiple options, but it is the simplest one to start from. The disadvantage is that you need to explicitly unwrap the parameters and wrap the results in Value - but you could probably later automate that using some reflection or type casting.

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

#1


Build yourself a discriminated union (DU) for the different sigs:

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

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

then make say Function<'a, 'b, 'c, 'd> list to hold your functions. This should work well enough, so long as the signatures are not too heterogeneous. Because you still require to have at most as many different kinds of signatures as you have cases in your DU.

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

A fully working example might look like this:

一个完整的工作示例可能如下所示:

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

#2


This depends on the context in which you are doing this, but you'll need to wrap the functions and the arguments in some way. You could box them and work with obj values (and then use reflection), you could wrap them in discriminated unions and you could likely do other things.

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

The discriminated union approach is probably the easiest. You could have a DU for different kinds of values that you support:

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

type Value =
  | Int of int
  | String of string

A function then takes a list of values and produces a value (you can make it option, because the function might fail if it gets incorrect arguments):

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

type Function = Value list -> Value option

To define your collection of functions, you can create a list. Each function will pattern match on the input to make sure it is getting the expected values:

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

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

Then you can create a list of arguments and call the function:

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

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

This is really just one of multiple options, but it is the simplest one to start from. The disadvantage is that you need to explicitly unwrap the parameters and wrap the results in Value - but you could probably later automate that using some reflection or type casting.

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