Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@
"template-curly-spacing": [
"error",
"never"
]
],
"prefer-template": ["error"]
}
}
9 changes: 9 additions & 0 deletions memoization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Memoization

Мемоизация - техника для оптимизации программ.

На практике, это обертка над функцией, запоминающая результат вычисления функции с конкретными аргументами. При повторном вызове с теми же аргументами, вычисление не произойдет, результат возьмется из кеша.

## Links

- <https://www.youtube.com/watch?v=H6S8QJo2Qxg>
43 changes: 43 additions & 0 deletions memoization/java-script/1-memoize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

// Reference: https://github.com/HowProgrammingWorks/Memoization/blob/master/JavaScript/1-memoize.js

// Для мемоизации нужно хранилище значений. Для этой задачи подойдет ассоциативный массив.
// В таком случае, нужен механизм, который позволит определить, что результат для конкретных аргументов уже вычислен.
// Нам нужна контрольная сумма. Как вариант: склеить строковое представление аргументов с типами аргументов.
const argKey = (x) => `${x.toString()}:${typeof x}`;
const generateKey = (args) => args.map(argKey).join('|');

const memoize = (fn) => {
// Нам нужен ассоциативный массив, который будет просто хранить значения.
// Поэтому избавляемся от прототипа
const cache = Object.create(null);

return (...args) => {
const key = generateKey(args);
const val = cache[key];
if (val) return val;
const res = fn(...args);
cache[key] = res;
return res;
};
};

// Usage
const sumSeq = (a, b) => {
console.log('Calculate sum');
let r = 0;
for (let i = a; i < b; i++) r += i;
return r;
};

const mSumSeq = memoize(sumSeq);

console.log('First call mSumSeq(2, 5)');
console.log('Value:', mSumSeq(2, 5));

console.log('Second call mSumSeq(2, 5)');
console.log('From cache:', mSumSeq(2, 5));

console.log('Call mSumSeq(2, 6)');
console.log('Calculated:', mSumSeq(2, 6));
42 changes: 42 additions & 0 deletions memoization/java-script/2-cache-size.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

// Reference: https://github.com/HowProgrammingWorks/Memoization/blob/master/JavaScript/3-cacheSize.js

const argKey = (x) => `${x.toString()}:${typeof x}`;
const generateKey = (args) => args.map(argKey).join('|');

const memoize = (fn, cacheSize) => {
const cache = new Map();

return (...args) => {
const key = generateKey(args);

if (cache.has(key)) return cache.get(key);

const result = fn(...args);

// Когда кеш переполнен, удаляем ключ с начала
if (cache.size >= cacheSize) {
const firstKey = cache.keys().next().value;
console.log('Delete key:', firstKey);
cache.delete(firstKey);
}

cache.set(key, result);
return result;
};
};

// Usage
const max = (a, b) => (a > b ? a : b);
const mMax = memoize(max, 3);

mMax(10, 8);
mMax(10, 8);
mMax(1, 15);
mMax(12, 3);
mMax(15, 2);
mMax(1, 15);
mMax(10, 8);
mMax(0, 0);
mMax(0, 0);
47 changes: 47 additions & 0 deletions memoization/java-script/exercises/1-a-expiration-cash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

// Task: implement time expiration cash

const argKey = (x) => `${x.toString()}:${typeof x}`;
const generateKey = (args) => args.map(argKey).join('|');

const memoize = (fn, cacheLifeTime) => {
const cache = new Map();

return (...args) => {
const key = generateKey(args);
const timeoutId = setTimeout(() => cache.delete(key), cacheLifeTime);

if (cache.has(key)) {
const value = cache.get(key);
console.log('From cache: ', value.result);

clearTimeout(value.timeoutId);
value.timeoutId = timeoutId;

return value.result;
};

const result = fn(...args);
cache.set(key, { timeoutId, result });
console.log('Calculated: ', result);
return result;
};
};

// Usage

const sum = (a, b) => a + b;
const memoizeSum = memoize(sum, 2000);

memoizeSum(1, 2);
memoizeSum(1, 2);
memoizeSum(1, 2);

memoizeSum(2, 4);
memoizeSum(2, 4);
setTimeout(() => memoizeSum(2, 4), 3000);
setTimeout(() => memoizeSum(2, 4), 5000);
setTimeout(() => memoizeSum(2, 4), 7000);
setTimeout(() => memoizeSum(2, 4), 9000);
setTimeout(() => memoizeSum(2, 4), 12000);
59 changes: 59 additions & 0 deletions memoization/java-script/exercises/1-b-expiration-cash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';

// Task: implement time expiration cash

const argKey = (x) => `${x.toString()}:${typeof x}`;
const generateKey = (args) => args.map(argKey).join('|');

const memoize = (fn, cacheLifeTime) => {
const cache = new Map();

const clearCache = () => setTimeout(() => cache.clear(), cacheLifeTime);
let timeoutId = clearCache();

return (...args) => {
const key = generateKey(args);

if (cache.has(key)) {
const value = cache.get(key);
console.log('From cache: ', value);

clearTimeout(timeoutId);
timeoutId = clearCache();

return value;
};

const result = fn(...args);
cache.set(key, result);
console.log('Calculated: ', result);
return result;
};
};

// Usage

const sum = (a, b) => a + b;
const memoizeSum = memoize(sum, 2000);

memoizeSum(1, 2); // Calculated
memoizeSum(1, 2); // From cache
memoizeSum(1, 2); // From cache

memoizeSum(2, 4); // Calculated
memoizeSum(2, 4); // From cache

setTimeout(() => {
memoizeSum(1, 2); // Calculated
memoizeSum(2, 4); // Calculated
}, 4000);

setTimeout(() => {
memoizeSum(1, 2); // From cache
memoizeSum(2, 4); // From cache
}, 6000);

setTimeout(() => {
memoizeSum(1, 2); // Calculated
memoizeSum(2, 4); // Calculated
}, 10000);
68 changes: 68 additions & 0 deletions memoization/java-script/exercises/2-max-records-count.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use strict';

// Task: Implement memoize with max records count and removing least used

const argKey = (x) => `${x.toString()}:${typeof x}`;
const generateKey = (args) => args.map(argKey).join('|');

const memoize = (fn, cacheSize) => {
const cache = new Map();

return (...args) => {
const key = generateKey(args);

if (cache.has(key)) {
const value = cache.get(key);
console.log('From cache: ', value.result);
value.usages += 1;

return value.result;
}

if (cache.size >= cacheSize) {
const maxValue = {};

for (const [key, value] of cache.entries()) {
if (typeof maxValue.value === 'undefined') {
maxValue.key = key;
maxValue.value = value;
continue;
}

if (value < maxValue.value) {
maxValue.key = key;
maxValue.value = value;
continue;
}
}

console.log('Deleting from cache: ', maxValue.key);
console.log('usages: ', maxValue.value.usages);
cache.delete(maxValue.key);
}

const result = fn(...args);
cache.set(key, { usages: 0, result });
console.log('Calculated: ', result);
return result;
};
};

// Usage

const sum = (a, b) => a + b;
const memoizeSum = memoize(sum, 3);

memoizeSum(1, 2);
memoizeSum(1, 2);

memoizeSum(2, 2);
memoizeSum(2, 2);
memoizeSum(2, 2);

memoizeSum(3, 3);
memoizeSum(3, 3);
memoizeSum(3, 3);
memoizeSum(3, 3);

memoizeSum(4, 4);
106 changes: 106 additions & 0 deletions memoization/java-script/exercises/4-max-total-data-size.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
'use strict';

// Task: Implement memoize with max total stored data size
// Max stored data in each field implementation

const typeToBytes = {
string: (string) => string.length * 2,
number: () => 8,
boolean: () => 4,
object: (object) => {
let bytes = 0;

for (const key of object) {
const value = object[key];
const handler = this[typeof value];

bytes += handler(value);
}

return bytes;
},
};

function convertSizeToBytes(size) {
const [value, memoryUnit] = getMemoryValueAndUnit(size);

const multiplicatorMap = {
T: 1000,
G: 1000,
M: 1000,
K: 1000,
B: 1,
};

const multiplicator = multiplicatorMap[memoryUnit] || 1;

return value * multiplicator;
}

function getMemoryValueAndUnit(size) {
const amount = parseFloat(size, 10);
const digitsNumber = String(amount).length;
const memoryUnit = size.slice(digitsNumber, digitsNumber + 1).toUpperCase();

return [amount, memoryUnit];
}


const argKey = (x) => `${x.toString()}:${typeof x}`;
const generateKey = (args) => args.map(argKey).join('|');

const memoize = (fn, maxCacheSize) => {
const cache = new Map();

return (...args) => {
const key = generateKey(args);

if (cache.has(key)) {
const value = cache.get(key);
console.log('From cache: ', value);

return value;
}

const availableBytes = convertSizeToBytes(maxCacheSize);
const result = fn(...args);
console.log('Calculated: ', result);
const resultSize = typeToBytes[typeof result](result);

if (resultSize > availableBytes) {
console.log(`Size of result more than cache can hold. Result size: ${resultSize}`);
return;
}

cache.set(key, result);
return result;
};
};

const sum = (a, b) => a + b;
const memoizedSum = memoize(sum, '20B');

memoizedSum('s', 'qsssss');

memoizedSum(1, 2);
memoizedSum(1, 2);

memoizedSum(true, false);

memoizedSum(2, 4);
memoizedSum(2, 4);
memoizedSum(2, 4);

memoizedSum(3, 4);

memoizedSum('22', 4);

memoizedSum('qqqqq', 'qqqqqqq');
memoizedSum('qqqqq', 'qqqqq');

memoizedSum(4, 4);

memoizedSum('q', 4);

memoizedSum(5, 4);
memoizedSum(5, 4);
Loading