gpt4 book ai didi

arrays - 如何在 F# 中生成具有动态类型的数组

转载 作者:行者123 更新时间:2023-12-04 04:54:12 25 4
gpt4 key购买 nike

我正在尝试编写一个函数来返回一个动态类型的数组。这是我尝试过的一种方法的示例:

let rng = System.Random()

type ConvertType =
| AsInts
| AsFloat32s
| AsFloats
| AsInt64s

type InputType =
| Ints of int[]
| Float32s of float32[]
| Floats of float[]
| Int64s of int64[]

let genData : int -> int -> ConvertType -> InputType * int[] =
fun (sCount:int) (rCount:int) (ct:ConvertType) ->
let source =
match ct with
| AsInts -> Array.init sCount (fun _ -> rng.Next()) |> Array.map (fun e -> int e) |> Ints
| AsFloat32s -> Array.init sCount (fun _ -> rng.Next()) |> Array.map (fun e -> float32 e) |> Float32s
| AsFloats -> Array.init sCount (fun _ -> rng.Next()) |> Array.map (fun e -> float e) |> Floats
| AsInt64s -> Array.init sCount (fun _ -> rng.Next()) |> Array.map (fun e -> int64 e) |> Int64s
let indices = Array.init rCount (fun _ -> rng.Next sCount) |> Array.sort
source, indices

我遇到的问题是,当我使用该函数时,我需要数组是原始类型,例如float32[] 而不是“InputType”。

我也尝试过通过内联函数创建的接口(interface)并使用泛型来做到这一点。我也无法让它按照我想要的方式工作,但我可能只是做错了。

编辑:
感谢您的出色回复,我今天必须尝试一下。我添加编辑是因为我解决了我的问题,尽管我没有按照我想要的方式解决它(即喜欢答案)。所以对于那些可能会看这个的人来说,我做了以下事情:
let counts = [100; 1000; 10000]
let itCounts = [ 1000; 500; 200]

let helperFunct =
fun (count:int) (numIt:int) (genData : int -> int -> ('T[] * int[] )) ->
let c2 = int( count / 2 )
let source, indices = genData count c2
....

[<Test>]
let ``int test case`` () =
let genData sCount rCount =
let source = Array.init sCount (fun _ -> rng.Next())
let indices = Array.init rCount (fun _ -> rng.Next sCount) |> Array.sort
source, indices

(counts, itCounts) ||> List.Iter2 (fun s i -> helperFunct s i genData)
.....

然后每个进行中的测试用例将类似于:
[<Test>]
let ``float test case`` () =
let genData sCount rCount =
let source = Array.init sCount (fun _ -> rng.Next()) |> Array.map (fun e -> float e)
let indices = Array.init rCount (fun _ -> rng.Next sCount) |> Array.sort
source, indices
.....

但是,我问这个问题的全部原因是我试图避免为每个测试用例重写 genData 函数。在我的真实代码中,这个临时解决方案使我不必分解“helperFunct”中的一些东西。

最佳答案

我觉得你现在的设计其实挺好的。通过明确列出支持的类型,您可以确保人们不会尝试调用该函数来生成无意义类型的数据(例如 byte )。

您可以使用 System.Convert 编写通用函数(它允许您将值转换为任意类型,但如果这没有意义,可能会失败)。它也(很可能)效率会降低,但我没有测量到:

let genDataGeneric<'T> sCount rCount : 'T[] * int[] =
let genValue() = System.Convert.ChangeType(rng.Next(), typeof<'T>) |> unbox<'T>
let source = Array.init sCount (fun _ -> genValue())
let indices = Array.init rCount (fun _ -> rng.Next sCount) |> Array.sort
source, indices

然后你可以写
genDataGeneric<float> 10 10

但人们也可以写 genDataGeneric<bool>并且代码要么崩溃要么产生废话,所以这就是为什么我认为你原来的方法也有它的好处。

或者,您可以参数化该函数以采用转换为 int 的转换器。 (这是你从 rng.Next 得到的)到你想要的类型:
let inline genDataGeneric convertor sCount rCount : 'T[] * int[] =
let genValue() = rng.Next() |> convertor
let source = Array.init sCount (fun _ -> genValue())
let indices = Array.init rCount (fun _ -> rng.Next sCount) |> Array.sort
source, indices

然后你可以只写 getDataGeneric float 10 10这仍然很优雅,而且效率也很高(我添加了 inline,因为我认为它可以在这里提供帮助)

编辑:根据 Leaf 的评论,我尝试使用 overloaded operator trick事实证明你也可以这样做( this blog has the best explanation 在下面奇怪的片段中发生了什么!):
// Specifies conversions that we want to allow
type Overloads = Overloads with
static member ($) (Overloads, fake:float) = fun (n:int) -> float n
static member ($) (Overloads, fake:int64) = fun (n:int) -> int64 n

let inline genDataGeneric sCount rCount : 'T[] * int[] =
let convert = (Overloads $ Unchecked.defaultof<'T>)
let genValue() = rng.Next() |> convert
let source = Array.init sCount (fun _ -> genValue())
let indices = Array.init rCount (fun _ -> rng.Next sCount) |> Array.sort
source, indices

let (r : float[] * _) = genDataGeneric 10 10 // This compiles & works
let (r : byte[] * _) = genDataGeneric 10 10 // This does not compile

这很有趣,但请注意,您仍然必须在某处指定类型(通过稍后使用或使用类型注释)。如果您使用类型注释,那么较早的代码可能更简单,并且提供的错误消息更少。但是这里使用的技巧肯定很有趣。

关于arrays - 如何在 F# 中生成具有动态类型的数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17002632/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com