let memoize f = let cache = System.Collections.Generic.Dictionary() fun x -> if cache.ContainsKey(x) then cache.[x] else let res = fx cache.[x] <- res res let rec memoizedFactorial = let factorial x = match x with | a when a = 0I -> 1I | x -> x * memoizedFactorial (x - 1I) memoize factorial let now = System.DateTime.Now let arguments = [10000I .. 10100I] let factorials = arguments |> List.map memoizedFactorial let timeDiff = System.DateTime.Now - now printfn "Time: %A" timeDiff
memoizedFactorial
function without arguments, and as a result we return another function, to which we later will pass arguments. Thus, it turns out that memoizedFactorial
exists in a single instance as an object and our cache will be the same for multiple function calls.memoizedFactorial
function is memoizedFactorial
. Why is the local function cache not recreated?main
function: public static void main@() { memoizedFactorial@11-1 = LazyExtensions.Create<FSharpFunc<BigInteger, BigInteger>>(new Program.clo@11-1()); memoizedFactorial@11 = Program.memoizedFactorial@11.get_Value(); now@18 = DateTime.Now; arguments@20 = SeqModule.ToList<BigInteger>(new Program.arguments@20(0, new BigInteger())); factorials@21 = ListModule.Map<BigInteger, BigInteger>(Program.memoizedFactorial, Program.arguments); timeDiff@23 = (TimeSpan) (DateTime.Now - Program.now); fp@1 = new PrintfFormat<FSharpFunc<TimeSpan, Unit>, TextWriter, Unit, Unit, TimeSpan>("Time: %A"); PrintfModule.PrintFormatLineToTextWriter<FSharpFunc<TimeSpan, Unit>>(Console.Out, Program.fp@1).Invoke(Program.timeDiff); }
memoizedFactorial
function memoizedFactorial
become a separate object. In addition, there are no variables at all - they all became fields of the class internal static class $Program
. In general, up to specific classes, the code should be clear. memoizedFactorial
into the memoizedFactorial
.$Program
class: internal static FSharpFunc<BigInteger, BigInteger> memoizedFactorial@11; internal static Lazy<FSharpFunc<BigInteger, BigInteger>> memoizedFactorial@11-1;
get_Value
method). internal class clo@11-1 : FSharpFunc<Unit, FSharpFunc<BigInteger, BigInteger>> { // Methods internal clo@11-1(); public override FSharpFunc<BigInteger, BigInteger> Invoke(Unit arg00@); }
public override FSharpFunc<BigInteger, BigInteger> Invoke(Unit arg00@) { return Program.memoizedFactorial@11-1(); }
memoizedFactorial@11-1
function. Finally, we are looking at this function: internal static FSharpFunc<BigInteger, BigInteger> memoizedFactorial@11-1() { return memoize<BigInteger, BigInteger>(new clo@13()); }
memoize
function. And before one more short circuit. A closure is a descendant of the same class; we immediately look at the Invoke method: public override BigInteger Invoke(BigInteger x) { if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<BigInteger>(x, BigInteger.Zero)) { return BigInteger.One; } return (x * Program.memoizedFactorial@11.get_Value().Invoke(x - BigInteger.One)); }
if
. We also note in passing that two BigInteger
compared through their hashes.memoize
function: public static FSharpFunc<a, b> memoize<a, b>(FSharpFunc<a, b> f) { return new memoize@3<a, b>(f, new Dictionary<a, b>()); }
internal class memoize@3<a, b> : FSharpFunc<a, b> { // Fields public Dictionary<a, b> cache; public FSharpFunc<a, b> f; // Methods internal memoize@3(FSharpFunc<a, b> f, Dictionary<a, b> cache); public override b Invoke(ax); }
Invoke
: public override b Invoke(ax) { if (this.cache.ContainsKey(x)) { return this.cache[x]; } b res = this.f.Invoke(x); this.cache[x] = res; return res; }
f
we already know through the fields of the object. The secret is solved.memoizedFactorial
function memoizedFactorial
what it is), functions like first-class citizens are actually just automatically generated classes and objects. I hope that my article will help someone better understand how F # works inside.Source: https://habr.com/ru/post/115727/
All Articles