Math.NET Numerics
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

200 lines
7.2 KiB

// (c) Microsoft Corporation. All rights reserved
#light
namespace Internal.Utilities.Collections
open System
open System.Collections.Generic
#nowarn "44" // This construct is deprecated. This F# library function has been renamed. Use 'isSome' instead
type ValueStrength<'T> =
| Strong of 'T
| Weak of WeakReference
type internal AgedLookup<'TKey,'TValue>(keepStrongly:int, ?areSame) =
/// The list of items stored. Youngest is at the end of the list.
/// The choice of order is somewhat aribtrary. If the other way then adding
/// items would be O(1) and removing O(N).
let mutable refs:('TKey*ValueStrength<'TValue>) list = []
// The 75 here determines how long the list should be passed the end of strongly held
// references. Some operations are O(N) and we don't want to let things get out of
// hand.
let keepTotal : int = max keepStrongly 75
/// Default behavior of areSame function is structural equality (=)
let AreSame(key,key') =
match areSame with
| Some(f)->f(key,key')
| None-> key = key'
/// Look up a the given key, return None if not found.
let TryPeekKeyValueImpl(data,key) =
let rec Lookup key = function
// Treat a list of key-value pairs as a lookup collection.
// This function returns true if two keys are the same according to the predicate
// function passed in.
| []->None
| (key',value)::t->
if AreSame(key,key') then Some(key',value)
else Lookup key t
Lookup key data
/// Determines whether a particular key exists.
let Exists(data,key) =
match TryPeekKeyValueImpl(data,key) with
| Some(_)->true
| None->false
/// Set a particular key's value.
let Add(data,key,value) =
data @ [key,value]
/// Remove a particular key value
let RemoveImpl(data, key) =
data |> List.filter (fun (key',_)->not (AreSame(key,key')))
/// Remove the stalest item from the list
let RemoveStalest(data) =
match data with
| (_::t)->t
| _->data
let TryGetKeyValueImpl(data,key) =
match TryPeekKeyValueImpl(data,key) with
| Some(_, value) as result ->
// If the result existed, move it to the top of the list.
let data = RemoveImpl(data,key)
let data = Add(data,key,value)
result,data
| None -> None,data
let FilterAndHold() =
[ for (key,value) in refs do
match value with
| Strong(value) -> yield (key,value)
| Weak(weakReference) ->
match weakReference.Target with
| null -> ()
| value -> yield key,(value:?>'TValue) ]
let AssignWithStrength(newdata) =
let actualLength = List.length newdata
let tossThreshold = max 0 (actualLength - keepTotal) // Delete everything less than this threshold
let weakThreshhold = max 0 (actualLength - keepStrongly) // Weaken everything less than this threshhold
refs<-
newdata
|> List.mapi( fun n kv -> n,kv ) // Place the index.
|> List.filter (fun (n:int,kv) -> n >= tossThreshold) // Delete everything below the toss threshhold
|> List.map( fun (n:int,(k,v)) -> k,if n<weakThreshhold then Weak(WeakReference(v)) else Strong(v) )
member al.TryPeekKeyValue(key) =
// Returns the original key value as well since it may be different depending on equality test.
let data = FilterAndHold()
TryPeekKeyValueImpl(data,key)
member al.TryGetKeyValue(key) =
let data = FilterAndHold()
let result,newdata = TryGetKeyValueImpl(data,key)
AssignWithStrength(newdata)
result
member al.TryGet(key) =
let data = FilterAndHold()
let result,newdata = TryGetKeyValueImpl(data,key)
AssignWithStrength(newdata)
match result with
| Some(key',value) -> Some(value)
| None -> None
member al.Put(key,value) =
let data = FilterAndHold()
let data = if Exists(data,key) then RemoveImpl(data,key) else data
let data = Add(data,key,value)
AssignWithStrength(data) // This will remove extras
member al.Remove(key) =
let data = FilterAndHold()
let newdata = RemoveImpl(data,key)
AssignWithStrength(newdata)
member al.MostRecent : ('TKey*'TValue) option=
let data = FilterAndHold()
if not data.IsEmpty then
// Non-optimal reverse list to get most recent. Consider an array of option for the data structure.
Some(data |> List.rev |> List.hd)
else None
member al.Clear() =
refs <- []
member al.ToSequence() =
FilterAndHold()
|> List.to_seq
type internal MruCache<'TKey,'TValue>(n,compute, ?areSame, ?isStillValid : 'TKey*'TValue->bool, ?areSameForSubsumption, ?logComputedNewValue, ?logUsedCachedValue) =
/// Default behavior of areSame function is structural equality (=)
let AreSame(key,key') =
match areSame with
| Some(f)->f(key,key')
| None-> key = key'
/// Default behavior of areSame function is structural equality (=)
let AreSameForSubsumption(key,key') : bool =
match areSameForSubsumption with
| Some(f)->f(key,key')
| None->AreSame(key,key')
/// The list of items in the cache. Youngest is at the end of the list.
/// The choice of order is somewhat aribtrary. If the other way then adding
/// items would be O(1) and removing O(N).
let cache = AgedLookup<'TKey,'TValue>(n,AreSameForSubsumption)
/// Whether or not this result value is still valid.
let IsStillValid(key,value) =
match isStillValid with
| Some(f)->f(key,value)
| None-> true
let Log = function
| Some(f) -> f
| None-> fun x->()
/// Log a message when a new value is computed.
let LogComputedNewValue = Log logComputedNewValue
/// Log a message when an existing value was retrieved from cache.
let LogUsedCachedValue = Log logUsedCachedValue
member bc.GetAvailable(key) =
match cache.TryPeekKeyValue(key) with
| Some(key', value)->
if AreSame(key',key) then Some(value)
else None
| None -> None
member bc.Get(key) =
let Compute() =
let value = compute key
cache.Put(key, value)
LogComputedNewValue(key)
value
match cache.TryGetKeyValue(key) with
| Some(key', value) as result ->
if AreSame(key', key) && IsStillValid(key,value) then
LogUsedCachedValue(key)
value
else Compute()
| None -> Compute()
member bc.MostRecent =
cache.MostRecent
member bc.SetAlternate(key:'TKey,value:'TValue) =
cache.Put(key,value)
member bc.Remove(key) =
cache.Remove(key)
member bc.Clear() =
cache.Clear()