diff --git a/JavaScript/8-timer.js b/JavaScript/8-timer.js new file mode 100644 index 0000000..95bcaff --- /dev/null +++ b/JavaScript/8-timer.js @@ -0,0 +1,60 @@ +'use strict'; + +const transKey = key => key.toString() + '(' + typeof(key) + ')'; +const createKey = keys => keys.map(key => transKey(key)).join('|'); + +const memoizeTime = ( + // Memoize the results of the fucntion + fn, //Function to memoize + timeout = 0 //Time in milliseconds, if not specify time is unlimited + //Returns: memoized function with time expiration cache +) => { + const cache = new Map(); + if (timeout) setTimeout(() => { + console.log('Clear cache'); + cache.clear(); + timeout = true; + }, timeout); + return (...args) => { + const key = createKey(args); + if (cache.has(key)) { + console.log( + `From cache:, ${fn.name}(${(args.join(', '))}) ${cache.get(key)}` + ); + return cache.get(key); + } + console.log(`Calculate: ${fn.name}(${args.join(', ')})`); + const res = fn(...args); + if (timeout !== true) cache.set(key, res); + return res; + }; +}; + +//Test +const fib = n => (n <= 2 ? 1 : fib(n - 1) + fib(n - 2)); + +const memoTimed = memoizeTime(fib, 500); +memoTimed(2); +memoTimed(5); +memoTimed(5); +memoTimed(6); +memoTimed(2); +memoTimed(2); +memoTimed(8); +memoTimed(9); +memoTimed(8); +memoTimed(9); +setTimeout(() => { + memoTimed(10); + memoTimed(2); + memoTimed(10); + memoTimed(3); + memoTimed(16); + memoTimed(3); + memoTimed(10); + memoTimed(3); + memoTimed(2); + memoTimed(2); + memoTimed(10); + memoTimed(10); +}, 1000); diff --git a/JavaScript/9-count.js b/JavaScript/9-count.js new file mode 100644 index 0000000..35244c5 --- /dev/null +++ b/JavaScript/9-count.js @@ -0,0 +1,68 @@ +'use strict'; + +const transKey = key => key.toString() + '(' + typeof(key) + ')'; +const createKey = keys => keys.map(key => transKey(key)).join('|'); + +const memoizeCount = ( + // Memoize the results of the fucntion + fn, //Function to memoize + length = 0 //Max record in the cache, if not specify records are unlimited + //Returns: memoized function with max records count and removing least used +) => { + const cache = new Map(); + return (...args) => { + const key = createKey(args); + if (cache.has(key)) { + const record = cache.get(key); + console.log( + `From cache:, ${fn.name}(${(args.join(', '))}) ${record.res}` + ); + record.count += 1; + return record.res; + } + console.log(`Calculate: ${fn.name}(${args.join(', ')})`); + const res = fn(...args); + if (length && cache.size >= length) { + let minUsage = length; + let minKey; + for (const [key, value] of cache.entries()) { + if (value.count < minUsage) { + minKey = key; + minUsage = value.count; + } + } + console.log(`Delete element: ${minKey}: ${cache.get(minKey).count}`); + cache.delete(minKey); + } + cache.set(key, { res, count: 0 }); + return res; + }; +}; + +//Test + +const fib = n => (n <= 2 ? 1 : fib(n - 1) + fib(n - 2)); + +const memoCounted = memoizeCount(fib, 7); +memoCounted(2); +memoCounted(5); +memoCounted(5); +memoCounted(6); +memoCounted(7); +memoCounted(7); +memoCounted(2); +memoCounted(9); +memoCounted(6); +memoCounted(2); +memoCounted(9); +memoCounted(2); +memoCounted(15); +memoCounted(3); +memoCounted(15); +memoCounted(3); +memoCounted(10); +memoCounted(10); +memoCounted(19); +memoCounted(18); +memoCounted(19); +memoCounted(10); diff --git a/JavaScript/a-size.js b/JavaScript/a-size.js new file mode 100644 index 0000000..7245d65 --- /dev/null +++ b/JavaScript/a-size.js @@ -0,0 +1,116 @@ +'use strict'; + +const transKey = key => key.toString() + '(' + typeof(key) + ')'; +const createKey = keys => keys.map(key => transKey(key)).join('|'); + +const sizeOf = ( + //Roughly calculate the size of objects + unit, //Unit of information + ...args //Array of object + //Returns: number represents the size of object in the units +) => { + let bytes = 0; + const count = (args) => { + args.forEach(el => size(el)); + }; + function size( + //Calculate size of object + obj //Object of value or reference type + //Returns: number represents the size of object in bytes + ) { + if (obj !== null && obj !== undefined) { + const type = typeof obj; + if (type === 'number') { + bytes += 8; + } else if (type === 'string') { + bytes += obj.length * 2; + } else if (type === 'boolean') { + bytes += 4; + } else if (type === 'object') { + const objClass = Object.prototype.toString.call(obj).slice(8, -1); + if (objClass === 'Object' || objClass === 'Array') { + for (const key in obj) { + if (!obj.hasOwnProperty(key)) continue; + size(obj[key]); + } + } else if (objClass === 'Map') { + for (const [key, value] of obj.entries()) { + size(key); + size(value); + } + } else bytes += obj.toString().length * 2; + } + } + } + const format = ( + //Convert bytes in the other units + number, //number of bytes + unit //unit in which convert + //Returns: size of object in the other units + ) => { + if (unit === 'bytes') return number; + if (unit === 'KiB') return (number / 1024).toFixed(5); + if (unit === 'MiB') return (number / 1048576).toFixed(5); + if (unit === 'GiB') return (number / 1073741824).toFixed(5); + throw new Error('Unknown units'); + }; + + count(args); + return format(bytes, unit); +}; + +const memoizeSize = ( + //Memoize the results of the fucntion + fn, //Function to memoize + maxSize = 0, //Max size of cache, if not specify size is unlimited + unit = 'bytes' //Unit of information, if not specify unit is bytes + //Returns: memoized function with max total stored data size +) => { + const cache = new Map(); + return (...args) => { + const key = createKey(args); + if (cache.has(key)) { + console.log( + `From cache:, ${fn.name}(${(args.join(', '))}) ${cache.get(key)}` + ); + return cache.get(key); + } + console.log(`Calculate: ${fn.name}(${args.join(', ')})`); + const res = fn(...args); + if (sizeOf(unit, cache, key, res) > maxSize && maxSize) { + const firstKey = cache.keys().next().value; + console.log(`Delete element: ${firstKey}: ${cache.get(firstKey)}`); + cache.delete(firstKey); + } + cache.set(key, res); + return res; + }; +}; + +//Tests + +const fib = n => (n <= 2 ? 1 : fib(n - 1) + fib(n - 2)); + +const memoSized = memoizeSize(fib, 200); +memoSized(2); +memoSized(4); +memoSized(5); +memoSized(6); +memoSized(7); +memoSized(2); +memoSized(3); +memoSized(9); +memoSized(8); +memoSized(2); +memoSized(11); +memoSized(2); +memoSized(14); +memoSized(3); +memoSized(16); +memoSized(3); +memoSized(10); +memoSized(13); +memoSized(19); +memoSized(18); +memoSized(10); +memoSized(10); diff --git a/JavaScript/b-univ.js b/JavaScript/b-univ.js new file mode 100644 index 0000000..42e5d8a --- /dev/null +++ b/JavaScript/b-univ.js @@ -0,0 +1,63 @@ +'use strict'; + +const fs = require('fs'); + +const transKey = key => key.toString() + '(' + typeof(key) + ')'; +const createKey = keys => keys.map(key => transKey(key)).join('|'); +const generateKey = (keys, cb) => { + keys = createKey(keys); + if (cb) keys = keys + '(' + cb.length + ')'; + else keys = keys + '(' + cb + ')'; + return keys; +}; + +const memoizeUniv = ( + //Memoize the result of both sync and async functions + fn, //Async or sync function to memoize + lib = null //Library of the functions + //Returns: memoized function +) => { + const cache = new Map(); + const f = lib ? lib[fn] : fn; + return (...args) => { + let cb = null; + if (typeof(args[args.length - 1]) === 'function') cb = args.pop(); + const key = generateKey(args, cb); + if (cache.has(key)) { + const record = cache.get(key); + if (cb) { + console.log('From cache with cb:', cb); + cb(record.err, record.data); + return; + } + console.log(`From cache:, ${f.name}(${(args.join(', '))}) ${record}`); + return record; + } + console.log(`Calculate: ${f.name}(${args.join(', ')})`); + if (cb) { + f(...args, (err, data) => { + cache.set(key, { err, data }); + cb(err, data); + }); + } else { + const res = f(...args); + cache.set(key, res); + return res; + } + }; +}; + +//Test + +const fib = n => (n <= 2 ? 1 : fib(n - 1) + fib(n - 2)); + +const memoSync = memoizeUniv(fib); +const memoAsync = memoizeUniv('readFile', fs); +console.log(memoSync(5)); +console.log(memoSync(5)); +memoAsync('4-async.js', 'utf8', (err, data) => { + console.log('data length:', data.length); + memoAsync('4-async.js', 'utf8', (err, data) => { + console.log('data length:', data.length); + }); +}); diff --git a/JavaScript/c-object.js b/JavaScript/c-object.js new file mode 100644 index 0000000..f1b9bb7 --- /dev/null +++ b/JavaScript/c-object.js @@ -0,0 +1,264 @@ +'use strict'; + +const fs = require('fs'); + +const transKey = key => key.toString() + '(' + typeof(key) + ')'; +const createKey = keys => keys.map(key => transKey(key)).join('|'); +const generateKey = (keys, cb) => { + keys = createKey(keys); + if (cb) keys = keys + '(' + cb.length + ')'; + else keys = keys + '(' + cb + ')'; + return keys; +}; + +const sizeOf = ( + //Roughly calculate the size of objects + unit, //Unit of information + ...args //Array of object + //Returns: number represents the size of object in the units +) => { + let bytes = 0; + const count = (args) => { + args.forEach(el => size(el)); + }; + function size( + //Calculate size of object + obj //Object of value or reference type + //Returns: number represents the size of object in bytes + ) { + if (obj !== null && obj !== undefined) { + const type = typeof obj; + if (type === 'number') { + bytes += 8; + } else if (type === 'string') { + bytes += obj.length * 2; + } else if (type === 'boolean') { + bytes += 4; + } else if (type === 'object') { + const objClass = Object.prototype.toString.call(obj).slice(8, -1); + if (objClass === 'Object' || objClass === 'Array') { + for (const key in obj) { + if (!obj.hasOwnProperty(key)) continue; + size(obj[key]); + } + } else if (objClass === 'Map') { + for (const [key, value] of obj.entries()) { + size(key); + size(value); + } + } else bytes += obj.toString().length * 2; + } + } + } + const format = ( + //Convert bytes in the other units + number, //number of bytes + unit //unit in which convert + //Returns: size of object in the other units + ) => { + if (unit === 'bytes') return number; + if (unit === 'KiB') return (number / 1024).toFixed(5); + if (unit === 'MiB') return (number / 1048576).toFixed(5); + if (unit === 'GiB') return (number / 1073741824).toFixed(5); + throw new Error('Unknown units'); + }; + + count(args); + return format(bytes, unit); +}; + +const memoizator = ( + //Memoize the results of the function + func, //Function to memize + lib = null //Library of the functions + //Returns: functional object +) => { + const cache = new Map(); + const events = new Map(); + const f = lib ? lib[func] : func; + const memo = (...args) => { + let cb = null; + if (typeof(args[args.length - 1]) === 'function') cb = args.pop(); + const key = generateKey(args, cb); + if (cache.has(key)) { + const record = memo.get(key); + record.count += 1; + if (cb) { + console.log('From cache with cb:', cb); + cb(record.err, record.data); + return; + } + console.log(`From cache: ${f.name}(${(args.join(', '))}) ${record.res}`); + return record.res; + } + if (memo.maxCount) memo.checkCount(); + console.log(`Calculate: ${f.name}(${args.join(', ')})`); + if (cb) { + f(...args, (err, data) => { + if (!memo.timeout) { + if (memo.size) memo.checkSize(cache, err, data, 'count', 0); + memo.add(key, { + err, + data, + count: 0, + }); + } + cb(err, data); + }); + } else { + const res = f(...args); + if (!memo.timeout) { + if (memo.size) memo.checkSize(cache, res, 'count', 0); + memo.add(key, { + res, + count: 0, + }); + } + return res; + } + }; + + const methods = { + clear() { + if (events.has('clear')) this.emit('clear'); + cache.clear(); + return this; + }, + add(key, value) { + if (!cache.has(key)) { + cache.set(key, value); + if (events.has('add')) this.emit('add', cache, key); + return this; + } + }, + del(key) { + if (cache.has(key)) { + if (events.has('del')) this.emit('del', cache, key); + cache.delete(key); + } + return this; + }, + get(key) { + if (cache.has(key)) return cache.get(key); + }, + setTime(msec) { + setTimeout(() => { + this.clear(); + this.timeout = true; + }, msec); + return this; + }, + setMaxSize(size, unit = 'bytes') { + this.size = size; + this.unit = unit; + this.checkSize(cache); + return this; + }, + checkSize(...args) { + while (sizeOf(this.unit, ...args) > this.size) { + console.log('\nSize check'); + const key = cache.keys().next().value; + this.del(key); + return this; + } + }, + setMaxCount(count) { + this.maxCount = count; + this.checkCount(); + return this; + }, + checkCount() { + while (cache.size >= this.maxCount) { + console.log('\nRecords check'); + let minUsage = this.maxCount; + let minKey = ''; + for (const [key, value] of cache.entries()) { + if (value.count < minUsage) { + minKey = key; + minUsage = value.count; + } + } + this.del(minKey); + } + return this; + }, + on(name, fn) { + const event = events.get(name); + if (event) event.push; + else events.set(name, [fn]); + return this; + }, + emit(name, ...data) { + const event = events.get(name); + event.forEach(fn => fn(...data)); + return this; + } + }; + + return Object.assign(memo, methods); +}; + +//Test +const fib = n => (n <= 2 ? 1 : fib(n - 1) + fib(n - 2)); + +const memo = memoizator('readFile', fs); + +memo('4-async.js', 'utf8', (err, data) => { + console.log('data length:', data.length); + memo('4-async.js', 'utf8', (err, data) => { + console.log('data length:', data.length); + }); +}); + +const memoized = memoizator(fib) + .setMaxCount(12) + .setTime(1000) + .setMaxSize(350) + .on('del', (cache, key) => console.log( + `Delete element: ${key}: ${JSON.stringify(cache.get(key))}`) + ); + +memoized(5); +memoized(5); +memoized(2); +memoized(6); +memoized(5); +memoized(6); +memoized(8); +memoized(2); +memoized(3); +memoized(9); +memoized(8); +memoized(6); +memoized + .on('add', (cache, key) => console.log( + `Add element: ${key}: ${JSON.stringify(cache.get(key))}`) + ) + .on('clear', () => console.log('Clear cache')); +setTimeout(() => { + memoized(2); + memoized(14); + memoized(14); + memoized(9); + memoized(19); + memoized(3); + memoized(10); + memoized(13); + memoized(19); + memoized(19); + memoized(10); + memoized(10); + memoized(19); + memoized(6); + memoized(2); + memoized.setMaxCount(5); + setTimeout(() => { + memoized(7); + memoized(20); + memoized(21); + memoized(7); + memoized(20); + memoized(21); + memoized(5); + }, 500); +}, 500); diff --git a/JavaScript/d-timeout.js b/JavaScript/d-timeout.js new file mode 100644 index 0000000..0a93cd2 --- /dev/null +++ b/JavaScript/d-timeout.js @@ -0,0 +1,63 @@ +'use strict'; + +const transKey = key => key.toString() + '(' + typeof(key) + ')'; +const createKey = keys => keys.map(key => transKey(key)).join('|'); + +const memoizeTime = ( + // Memoize the results of the fucntion + fn, //Function to memoize + msec //Time for each element in cache in milliseconds + //Returns: memoized function with limited time in the cache for each element +) => { + const cache = new Map(); + return (...args) => { + const key = createKey(args); + const timeout = setTimeout(() => { + console.log(`Delete: ${key}`); + cache.delete(key); + }, msec); + if (cache.has(key)) { + const record = cache.get(key); + clearTimeout(record.timeout); + record.timeout = timeout; + console.log( + `From cache:, ${fn.name}(${(args.join(', '))}) ${record.res}` + ); + return record.res; + } + console.log(`Calculate: ${fn.name}(${args.join(', ')})`); + const res = fn(...args); + cache.set(key, { res, timeout }); + return res; + }; +}; + +const fib = n => (n <= 2 ? 1 : fib(n - 1) + fib(n - 2)); + +const memoTimed = memoizeTime(fib, 300); +memoTimed(2); +memoTimed(5); +memoTimed(5); +memoTimed(6); +memoTimed(2); +memoTimed(9); +memoTimed(8); +memoTimed(10); +memoTimed(8); +memoTimed(9); +setTimeout(() => { + memoTimed(10); + memoTimed(6); + memoTimed(10); + memoTimed(3); + memoTimed(16); + memoTimed(3); + setTimeout(() => { + memoTimed(10); + memoTimed(3); + memoTimed(2); + memoTimed(2); + memoTimed(10); + memoTimed(10); + }, 200); +}, 200);