首页 > 解决方案 > 使用 bigint 查找扩展 F# 数组

问题描述

我想扩展 F# 数组,这样我就可以使用数组而不转换为有限的int. 相反,我想bigint直接使用。

我能够向数组类型添加第二个长度方法,如下所示:

type 'T ``[]`` with

  member this.LengthI: bigint = 
    bigint this.Length

  member this.Item(index: bigint): 'T = 
    this.[int index]

但是,Item无法使用.[ ]语法调用该方法。

有什么想法可以实现吗?我这可能吗?

标签: f#

解决方案


我强烈怀疑这对于本机数组是不可能的。您可以验证自己是否可以为其他集合重载索引访问。

如果编译以下代码:

let myArray = [| "a" |]
let myList = [ "a" ]

let arrayElement = myArray.[11111]
let listElement = myList.[22222]

并检查生成的 IL,您会看到在访问列表元素时编译为常规虚拟调用,有一个特殊的 CIL 指令用于访问本机数组元素,ldelem.

//000004: let arrayElement = myArray.[11111]
    IL_002c:  call       string[] Fuduoqv1565::get_myArray()
    IL_0031:  ldc.i4     0x2b67
    IL_0036:  ldelem     [mscorlib]System.String
    IL_003b:  stsfld     string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::arrayElement@4
    .line 5,5 : 1,33 ''
//000005: let listElement = myList.[22222]
    IL_0040:  call       class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string> Fuduoqv1565::get_myList()
    IL_0045:  ldc.i4     0x56ce
    IL_004a:  callvirt   instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>::get_Item(int32)
    IL_004f:  stsfld     string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::listElement@5
    IL_0054:  ret

我猜想特殊情况数组访问该单个指令的相同编译器逻辑也会绕过任何涉及扩展方法等的重载解决方案。

避免这种情况的一种方法是将数组包装在自定义类型中,其中重载的索引器将按预期工作。在大多数情况下,将包装器类型设为结构应该可以减少性能损失:

type [<Struct>] BigArray<'T>(array : 'T[]) = 

  member this.LengthI: bigint = 
    bigint array.Length

  member this.Item
      with get(index : int) = array.[index]
      and set (index : int) value = array.[index] <- value

  member this.Item
      with get(index : bigint) = array.[int index]
      and set (index : bigint) value = array.[int index] <- value

let bigArray = BigArray myArray
let bigArrayElement = bigArray.[0]
let bigArrayElement2 = bigArray.[bigint 0]

另一种是将数组向上转换为基System.Array类,然后您可以在其上定义相同的重载运算符。这消除了创建包装器类型和复制 的所有成员的需要'T[],因为您可以根据需要向上/向下转换相同的数组对象。但是,由于基类是无类型的,因此您将失去类型安全性,并且在使用索引访问时必须对元素进行装箱/拆箱,这非常难看:

type System.Array with

  member this.Item
      with get (index : int) = (this :?> 'T[]).[index]
      and set  (index : int) (value : 'T) = (this :?> 'T[]).[index] <- value

  member this.Item
      with get(index : bigint) : 'T  = (this :?> 'T[]).[int index]
      and set(index : bigint) (value : 'T) = (this :?> 'T[]).[int index] <- value

let untypedArray = myArray :> System.Array
let untypedArrayElement = box untypedArray.[0] :?> string
let untypedArrayElement2 = box untypedArray.[bigint 0] :?> string

推荐阅读