集合

时间:2017-10-03 05:08:03

标签: f# functional-programming mutable conceptual

我对函数式编程很陌生,所以这可能是一个由于误解而引起的问题,但是我无法理解这一点 - 从OOP的角度来看它似乎很明显......

情形: 假设您有一个演员或微服务,如架构方法,其中消息/请求被发送到处理它们并回复的某些组件。现在假设,其中一个组件存储来自未来请求请求的一些数据(例如,它计算一个值并将其存储在高速缓存中,以便下次发生相同的请求时,不需要进行计算)。 数据可以保存在内存中。

问题: 你如何在函数式编程中,特别是在f#中处理这样的场景?我想静态字典不是一种功能性方法,如果可能的话,我不想包含任何外部的东西,比如数据存储。

或更准确: 如果应用创建数据将在稍后的处理中再次使用,我们存储数据在哪里?

示例:您有一个应用程序在某些初始数据上执行某种任务。首先,存储初始数据(例如将其添加到字典中),然后执行基于数据子集执行某些处理的第一个任务,然后执行添加的第二个任务数据等等,直到所有任务完成......

现在基本方法(根据我的理解)将定义数据并将任务用作转发处理数据的某种处理链,如initial-data -> task-1 -> task-2 -> ... -> done 但这不符合获取/添加数据基于消息和异步的架构。

方法

我最初的做法是这个

type Record = { }

let private dummyStore = new System.Collections.Concurrent.ConcurrentBag<Record>()

let search comparison =
    let matchingRecords = dummyStore |> Seq.where (comparison)
    if matchingRecords |> Seq.isEmpty
    then EmptyFailedRequest
    else Record (matchingRecords |> Seq.head)

let initialize initialData = 
    initialData |> Seq.iter (dummyStore.Add)

let add newRecord =
    dummyStore.Add(newRecord) 

封装在一个看起来像OOP方法的模块中。

在@Gustavo邀请我提供一个例子并考虑他的建议后,我意识到我可以这样做(更高一级到实际调用函数的地方):

let handleMessage message store = 
    // all the operations from above but now with Seq<Record> -> ... -> Seq<Record>
    store

let agent = MailboxProcessor.Start(fun inbox-> 

    let rec messageLoop store = async{
        let! msg = inbox.Receive()

        let modifiedStore = handleMessage msg store

        return! messageLoop modifiedStore 
        }
    messageLoop Seq.empty
    )

这对我来说很好地回答了这个问题,因为它完全取消了可变性和共享状态。但是当我看到第一种方法时,我想不出任何解决方案没有功能之外的集合

请注意,这个问题在f#中解释环境,语法等。我不想要一个有效的解决方案,因为f#是多范式的,我想得到一个功能性的方法。

到目前为止,我已经阅读了我在SO上可以找到的所有问题,但他们要么证明理论上的可能性,要么他们使用集合来实现这种情况 - 如果重复请点我是正确的方向。

1 个答案:

答案 0 :(得分:4)

您可以使用名为memoization的技术,这在FP中非常常见。 它恰好包括使用计算值保存字典。

以下是一个示例实现:

open System
open System.Collections.Concurrent

let getOrAdd (a:ConcurrentDictionary<'A,'B>) (b:_->_) k = a.GetOrAdd(k, b)

let memoize f =
    let dic = new ConcurrentDictionary<_,_>()
    getOrAdd dic f

请注意,使用memoize,您可以修饰任何函数并获取它的备忘版本。这是一个示例:

let f x =
    printfn "calculating f (%i)" x
    2 * x

let g = memoize f // g is the memoized version of f

// test

> g 5 ;;
calculating f (5)
val it : int = 10

> g 5 ;;
val it : int = 10

您可以看到,在第二次执行中,未计算该值。