diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..3d64647 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,9 @@ +# Files and directories created by pub +.dart_tool/ +.packages + +# Conventional directory for build outputs +build/ + +# Directory created by dartdoc +doc/api/ diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..d482b5a --- /dev/null +++ b/example/README.md @@ -0,0 +1 @@ +A simple example showcase of memoize library diff --git a/example/bin/example.dart b/example/bin/example.dart new file mode 100644 index 0000000..0c2bc0c --- /dev/null +++ b/example/bin/example.dart @@ -0,0 +1,40 @@ +import 'dart:math' as math; + +import 'package:memoize/memoize.dart'; + +num heavyCalc(num number) { + num sum = 0; + for (int x = 0; x < 999999; x++) { + sum += math.sqrt(number); + } + return sum; +} + +void main(List arguments) { + const loops = 99; + print('Some heavy calculations done $loops times with 6 different inputs:'); + List numbers = [10000, 4206969, 777, 21372137, 99999999, 10000]; + var sumNormal = 0.0; + var w = Stopwatch()..start(); + for (int x = 0; x < loops; x++) { + for (var n in numbers) { + sumNormal += heavyCalc(n); + } + } + print('\tNo cache: ${w.elapsedMilliseconds}ms'); + + var sumCache = 0.0; + w.reset(); + var cacheSqrt = memo1(heavyCalc); + for (int x = 0; x < loops; x++) { + for (var n in numbers) { + sumCache += cacheSqrt(n); + } + } + print('\tWith cache: ${w.elapsedMilliseconds}ms'); + if (sumNormal == sumCache) { + print("...and results match!"); + } else { + print("...but results don't match... guess we broke something :D"); + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..d5af34f --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,9 @@ +name: example +description: Example showcase of memoize library + +environment: + sdk: '>=2.12.0 <3.0.0' + +dependencies: + memoize: + path: ../ diff --git a/lib/memoize.dart b/lib/memoize.dart index d88bad1..c31c3b7 100644 --- a/lib/memoize.dart +++ b/lib/memoize.dart @@ -1,5 +1,25 @@ +import 'dart:collection'; + import 'function_defs.dart'; +// Stolen from flutter +// https://api.flutter.dev/flutter/foundation/listEquals.html +bool _listEquals(List? a, List? b) { + if (a == null) return b == null; + if (b == null || a.length != b.length) return false; + if (identical(a, b)) return true; + for (int index = 0; index < a.length; index += 1) { + if (a[index] != b[index]) return false; + } + return true; +} + +int _listHashCode(List list) { + var hashCode = 0; + for (final el in list) hashCode = hashCode ^ el.hashCode; + return hashCode; +} + /// Lazy evaluates function and returns cached result on each call. Func0 memo0(Func0 func) { late R prevResult; @@ -20,19 +40,19 @@ Func0 memo0(Func0 func) { /// Checks 1 argument for equality with [==] operator and returns the cached /// result if it was not changed. Func1 memo1(Func1 func) { - late A prevArg; - late R prevResult; - bool isInitial = true; - - return ((A arg) { - if (!isInitial && arg == prevArg) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA) { + final cached = argsToOutput[[aA]]; + if (cached != null) { + return cached; } else { - prevArg = arg; - prevResult = func(arg); - isInitial = false; - - return prevResult; + final res = func(aA); + argsToOutput[[aA]] = res; + return res; } }); } @@ -40,21 +60,19 @@ Func1 memo1(Func1 func) { /// Checks 2 arguments for equality with [==] operator and returns the cached /// result if they were not changed. Func2 memo2(Func2 func) { - late A prevArgA; - late B prevArgB; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB) { - if (!isInitial && argA == prevArgA && argB == prevArgB) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB) { + final cached = argsToOutput[[aA, aB]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevResult = func(argA, argB); - isInitial = false; - - return prevResult; + final res = func(aA, aB); + argsToOutput[[aA, aB]] = res; + return res; } }); } @@ -62,26 +80,19 @@ Func2 memo2(Func2 func) { /// Checks 3 arguments for equality with [==] operator and returns the cached /// result if they were not changed. Func3 memo3(Func3 func) { - late A prevArgA; - late B prevArgB; - late C prevArgC; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB, C argC) { - if (!isInitial && - argA == prevArgA && - argB == prevArgB && - argC == prevArgC) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB, C aC) { + final cached = argsToOutput[[aA, aB, aC]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevArgC = argC; - prevResult = func(argA, argB, argC); - isInitial = false; - - return prevResult; + final res = func(aA, aB, aC); + argsToOutput[[aA, aB, aC]] = res; + return res; } }); } @@ -89,29 +100,19 @@ Func3 memo3(Func3 func) { /// Checks 4 arguments for equality with [==] operator and returns the cached /// result if they were not changed. Func4 memo4(Func4 func) { - late A prevArgA; - late B prevArgB; - late C prevArgC; - late D prevArgD; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB, C argC, D argD) { - if (!isInitial && - argA == prevArgA && - argB == prevArgB && - argC == prevArgC && - argD == prevArgD) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB, C aC, D aD) { + final cached = argsToOutput[[aA, aB, aC, aD]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevArgC = argC; - prevArgD = argD; - prevResult = func(argA, argB, argC, argD); - isInitial = false; - - return prevResult; + final res = func(aA, aB, aC, aD); + argsToOutput[[aA, aB, aC, aD]] = res; + return res; } }); } @@ -119,32 +120,19 @@ Func4 memo4(Func4 func) { /// Checks 5 arguments for equality with [==] operator and returns the cached /// result if it was not changed. Func5 memo5(Func5 func) { - late A prevArgA; - late B prevArgB; - late C prevArgC; - late D prevArgD; - late E prevArgE; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB, C argC, D argD, E argE) { - if (!isInitial && - argA == prevArgA && - argB == prevArgB && - argC == prevArgC && - argD == prevArgD && - argE == prevArgE) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB, C aC, D aD, E aE) { + final cached = argsToOutput[[aA, aB, aC, aD, aE]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevArgC = argC; - prevArgD = argD; - prevArgE = argE; - prevResult = func(argA, argB, argC, argD, argE); - isInitial = false; - - return prevResult; + final res = func(aA, aB, aC, aD, aE); + argsToOutput[[aA, aB, aC, aD, aE]] = res; + return res; } }); } @@ -153,35 +141,19 @@ Func5 memo5(Func5 func) { /// result if it was not changed. Func6 memo6( Func6 func) { - late A prevArgA; - late B prevArgB; - late C prevArgC; - late D prevArgD; - late E prevArgE; - late F prevArgF; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB, C argC, D argD, E argE, F argF) { - if (!isInitial && - argA == prevArgA && - argB == prevArgB && - argC == prevArgC && - argD == prevArgD && - argE == prevArgE && - argF == prevArgF) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB, C aC, D aD, E aE, F aF) { + final cached = argsToOutput[[aA, aB, aC, aD, aE, aF]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevArgC = argC; - prevArgD = argD; - prevArgE = argE; - prevArgF = argF; - prevResult = func(argA, argB, argC, argD, argE, argF); - isInitial = false; - - return prevResult; + final res = func(aA, aB, aC, aD, aE, aF); + argsToOutput[[aA, aB, aC, aD, aE, aF]] = res; + return res; } }); } @@ -190,38 +162,19 @@ Func6 memo6( /// result if it was not changed. Func7 memo7( Func7 func) { - late A prevArgA; - late B prevArgB; - late C prevArgC; - late D prevArgD; - late E prevArgE; - late F prevArgF; - late G prevArgG; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB, C argC, D argD, E argE, F argF, G argG) { - if (!isInitial && - argA == prevArgA && - argB == prevArgB && - argC == prevArgC && - argD == prevArgD && - argE == prevArgE && - argF == prevArgF && - argG == prevArgG) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB, C aC, D aD, E aE, F aF, G aG) { + final cached = argsToOutput[[aA, aB, aC, aD, aE, aF, aG]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevArgC = argC; - prevArgD = argD; - prevArgE = argE; - prevArgF = argF; - prevArgG = argG; - prevResult = func(argA, argB, argC, argD, argE, argF, argG); - isInitial = false; - - return prevResult; + final res = func(aA, aB, aC, aD, aE, aF, aG); + argsToOutput[[aA, aB, aC, aD, aE, aF, aG]] = res; + return res; } }); } @@ -230,41 +183,19 @@ Func7 memo7( /// result if it was not changed. Func8 memo8( Func8 func) { - late A prevArgA; - late B prevArgB; - late C prevArgC; - late D prevArgD; - late E prevArgE; - late F prevArgF; - late G prevArgG; - late H prevArgH; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB, C argC, D argD, E argE, F argF, G argG, H argH) { - if (!isInitial && - argA == prevArgA && - argB == prevArgB && - argC == prevArgC && - argD == prevArgD && - argE == prevArgE && - argF == prevArgF && - argG == prevArgG && - argH == prevArgH) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB, C aC, D aD, E aE, F aF, G aG, H aH) { + final cached = argsToOutput[[aA, aB, aC, aD, aE, aF, aG, aH]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevArgC = argC; - prevArgD = argD; - prevArgE = argE; - prevArgF = argF; - prevArgG = argG; - prevArgH = argH; - prevResult = func(argA, argB, argC, argD, argE, argF, argG, argH); - isInitial = false; - - return prevResult; + final res = func(aA, aB, aC, aD, aE, aF, aG, aH); + argsToOutput[[aA, aB, aC, aD, aE, aF, aG, aH]] = res; + return res; } }); } @@ -273,45 +204,19 @@ Func8 memo8( /// result if it was not changed. Func9 memo9( Func9 func) { - late A prevArgA; - late B prevArgB; - late C prevArgC; - late D prevArgD; - late E prevArgE; - late F prevArgF; - late G prevArgG; - late H prevArgH; - late I prevArgI; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB, C argC, D argD, E argE, F argF, G argG, H argH, - I argI) { - if (!isInitial && - argA == prevArgA && - argB == prevArgB && - argC == prevArgC && - argD == prevArgD && - argE == prevArgE && - argF == prevArgF && - argG == prevArgG && - argH == prevArgH && - argI == prevArgI) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB, C aC, D aD, E aE, F aF, G aG, H aH, I aI) { + final cached = argsToOutput[[aA, aB, aC, aD, aE, aF, aG, aH, aI]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevArgC = argC; - prevArgD = argD; - prevArgE = argE; - prevArgF = argF; - prevArgG = argG; - prevArgH = argH; - prevArgI = argI; - prevResult = func(argA, argB, argC, argD, argE, argF, argG, argH, argI); - isInitial = false; - - return prevResult; + final res = func(aA, aB, aC, aD, aE, aF, aG, aH, aI); + argsToOutput[[aA, aB, aC, aD, aE, aF, aG, aH, aI]] = res; + return res; } }); } @@ -320,49 +225,19 @@ Func9 memo9( /// result if it was not changed. Func10 memo10( Func10 func) { - late A prevArgA; - late B prevArgB; - late C prevArgC; - late D prevArgD; - late E prevArgE; - late F prevArgF; - late G prevArgG; - late H prevArgH; - late I prevArgI; - late J prevArgJ; - late R prevResult; - bool isInitial = true; - - return ((A argA, B argB, C argC, D argD, E argE, F argF, G argG, H argH, - I argI, J argJ) { - if (!isInitial && - argA == prevArgA && - argB == prevArgB && - argC == prevArgC && - argD == prevArgD && - argE == prevArgE && - argF == prevArgF && - argG == prevArgG && - argH == prevArgH && - argI == prevArgI && - argJ == prevArgJ) { - return prevResult; + final argsToOutput = HashMap( + equals: (a, b) => _listEquals(a, b), + hashCode: (l) => _listHashCode(l), + ); + + return ((A aA, B aB, C aC, D aD, E aE, F aF, G aG, H aH, I aI, J aJ) { + final cached = argsToOutput[[aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ]]; + if (cached != null) { + return cached; } else { - prevArgA = argA; - prevArgB = argB; - prevArgC = argC; - prevArgD = argD; - prevArgE = argE; - prevArgF = argF; - prevArgG = argG; - prevArgH = argH; - prevArgI = argI; - prevArgJ = argJ; - prevResult = - func(argA, argB, argC, argD, argE, argF, argG, argH, argI, argJ); - isInitial = false; - - return prevResult; + final res = func(aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ); + argsToOutput[[aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ]] = res; + return res; } }); } diff --git a/test/memoize_test.dart b/test/memoize_test.dart index 1aa6a8a..5ded087 100644 --- a/test/memoize_test.dart +++ b/test/memoize_test.dart @@ -663,8 +663,8 @@ void main() { expect(func(rect1, rect2, rect3, rect4, rect5, rect6, rect7), 1); expect(func(rect1, rect2, rect3, rect4, rect4, rect6, rect8), 2); expect(func(rect1, rect2, rect3, rect4, rect4, rect4, rect8), 2); - expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect7), 3); - expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect4), 3); + expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect7), 1); + expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect4), 1); }); }); @@ -798,8 +798,8 @@ void main() { expect(func(rect1, rect1, rect3, rect4, rect5, rect1, rect7, rect8), 1); expect(func(rect1, rect2, rect2, rect4, rect4, rect6, rect8, rect9), 2); expect(func(rect1, rect2, rect3, rect1, rect4, rect4, rect4, rect9), 2); - expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect7, rect1), 3); - expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect4, rect1), 3); + expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect7, rect1), 1); + expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect4, rect1), 1); }); }); @@ -940,8 +940,8 @@ void main() { expect(func(rect1, rect1, rect3, rect4, rect5, rect1, rect7, rect8, rect9), 1); expect(func(rect1, rect2, rect2, rect4, rect4, rect6, rect8, rect9, rect10), 2); expect(func(rect1, rect2, rect3, rect1, rect4, rect4, rect4, rect9, rect10), 2); - expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect7, rect1, rect1), 3); - expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect4, rect1, rect2), 3); + expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect7, rect1, rect1), 1); + expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect4, rect1, rect2), 1); }); }); @@ -1091,8 +1091,8 @@ void main() { expect(func(rect1, rect1, rect3, rect4, rect5, rect1, rect7, rect8, rect9, rect10), 1); expect(func(rect1, rect2, rect2, rect4, rect4, rect6, rect8, rect9, rect10, rect11), 2); expect(func(rect1, rect2, rect3, rect1, rect4, rect4, rect4, rect9, rect10, rect11), 2); - expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect7, rect1, rect1, rect10), 3); - expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect4, rect1, rect2, rect10), 3); + expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect7, rect1, rect1, rect10), 1); + expect(func(rect2, rect2, rect3, rect3, rect6, rect1, rect4, rect1, rect2, rect10), 1); }); });