F# 闭包:地图引用单元格始终为空

发布于 2024-09-25 04:08:34 字数 895 浏览 5 评论 0原文

给出以下代码:

static member private getIntValue (_map:Map<string, int>) (_key:string) =
    if (_map.ContainsKey _key) then
        _map.[_key], _map
    else
        let i = doSomething ()
        i, if i > 0 then _map.Add (_key, i) else _map

static member private getDataFn<'T> (_getFn:Map<string, 'T> -> string -> 'T * Map<string, 'T>) =
    let dataMap = ref Map.empty
    fun _key ->
        let value, updatedMap = _getFn !dataMap _key
        dataMap := updatedMap
        value

static member getIndexNumber = getDataFn<int> getIntValue

... getDataFn<'T> 的函数定义第一行(即 fun _key -> ...)中 dataMap 引用单元格的值无论我调用 getIndexNumber 多少次,它始终为空(即 dataMap.Count = 0)。

显然,我希望每当将不存在的键传递给 getIndexNumber 时 dataMap 都会更新,但这并没有发生。每次都会将一个空 Map 传递给 getIntValue。

为什么会发生这种情况?

(PS,我知道我可以只使用字典,但这不是重点。)

Given the following code:

static member private getIntValue (_map:Map<string, int>) (_key:string) =
    if (_map.ContainsKey _key) then
        _map.[_key], _map
    else
        let i = doSomething ()
        i, if i > 0 then _map.Add (_key, i) else _map

static member private getDataFn<'T> (_getFn:Map<string, 'T> -> string -> 'T * Map<string, 'T>) =
    let dataMap = ref Map.empty
    fun _key ->
        let value, updatedMap = _getFn !dataMap _key
        dataMap := updatedMap
        value

static member getIndexNumber = getDataFn<int> getIntValue

... the value of the dataMap reference cell in the first line of the function definition (i.e. fun _key -> ...) of getDataFn<'T> is always empty (i.e. dataMap.Count = 0), no matter how many times I call getIndexNumber.

Obviously, I'm expecting dataMap to be updated whenever a non-existent key is passed to getIndexNumber, but this isn't happening. An empty Map is being passed to getIntValue every time.

Why is this happening?

(P.S. I know that I could just use a Dictionary, that's beside the point.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

拥抱我好吗 2024-10-02 04:08:34

这里有一些代码可以解禁:

let doSomething() = 2
type Foo() =
    static let indexer = Foo.getDataFn<int> Foo.getIntValue 
    static member private getIntValue (_map:Map<string, int>) (_key:string) = 
        if (_map.ContainsKey _key) then 
            _map.[_key], _map 
        else 
            let i = doSomething () 
            i, if i > 0 then _map.Add (_key, i) else _map 

    static member private getDataFn<'T> (_getFn:Map<string, 'T> -> string -> 'T * Map<string, 'T>) : (string -> 'T)= 
        let dataMap = ref Map.empty 
        fun _key -> 
            printfn "count = %d" (!dataMap).Count 
            let value, updatedMap = _getFn !dataMap _key 
            dataMap := updatedMap 
            value 

    static member getIndexNumber = indexer
//    static member getIndexNumber = Foo.getDataFn<int> Foo.getIntValue 

let r = Foo.getIndexNumber("foo")
printfn "%d" r
let r2 = Foo.getIndexNumber("foo")
printfn "%d" r2

关键是 getIndexNumber 是一个属性,每次调用它时都会重新计算,最终每次都会调用 getDataFn ,从而分配每次都有一个新的ref。我将对 getDataFn 的调用移至 static let 中,因此它只被调用一次。

不过,这段代码看起来非常不习惯。我可以建议更像这样的代码吗?

let doSomething() = 2

type StringIndexer() =
    let mutable map : Map<string,int> = Map.empty 
    let get(s) =
        printfn "count = %d" map.Count 
        match map.TryFind s with
        | None -> 
            let i = doSomething()
            map <- map.Add(s,i)
            i
        | Some(i) -> i
    member this.GetIndexNumber(s) = get(s)

let si = new StringIndexer()
let r = si.GetIndexNumber("foo")
printfn "%d" r
let r2 = si.GetIndexNumber("foo")
printfn "%d" r2

Here's some code to unblock you:

let doSomething() = 2
type Foo() =
    static let indexer = Foo.getDataFn<int> Foo.getIntValue 
    static member private getIntValue (_map:Map<string, int>) (_key:string) = 
        if (_map.ContainsKey _key) then 
            _map.[_key], _map 
        else 
            let i = doSomething () 
            i, if i > 0 then _map.Add (_key, i) else _map 

    static member private getDataFn<'T> (_getFn:Map<string, 'T> -> string -> 'T * Map<string, 'T>) : (string -> 'T)= 
        let dataMap = ref Map.empty 
        fun _key -> 
            printfn "count = %d" (!dataMap).Count 
            let value, updatedMap = _getFn !dataMap _key 
            dataMap := updatedMap 
            value 

    static member getIndexNumber = indexer
//    static member getIndexNumber = Foo.getDataFn<int> Foo.getIntValue 

let r = Foo.getIndexNumber("foo")
printfn "%d" r
let r2 = Foo.getIndexNumber("foo")
printfn "%d" r2

The key is that getIndexNumber is a property, which is re-evaluated each time it's called, which ends up calling getDataFn each time, which allocates a fresh ref each time. I moved the call to getDataFn into a static let so it only gets called once.

This code all looks very un-idiomatic, though. Might I suggest code more like this?

let doSomething() = 2

type StringIndexer() =
    let mutable map : Map<string,int> = Map.empty 
    let get(s) =
        printfn "count = %d" map.Count 
        match map.TryFind s with
        | None -> 
            let i = doSomething()
            map <- map.Add(s,i)
            i
        | Some(i) -> i
    member this.GetIndexNumber(s) = get(s)

let si = new StringIndexer()
let r = si.GetIndexNumber("foo")
printfn "%d" r
let r2 = si.GetIndexNumber("foo")
printfn "%d" r2
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文