安全数学 Class。如何创建链式计算?
SafeMath Class. How to create chainable calculations?
我很难找到用于在线搜索此内容的关键字。
我创建了一个具有安全数学函数的 class。每个函数都有 2 个参数,在通过断言求值后,它 returns 结果。
示例:
class SafeMath {
static add(x: number, y: number) {
let z: number = x + y;
assert(z >= x, 'ds-math-add-overflow');
return z;
}
static sub(x: number, y: number) {
let z: number = x - y;
assert(z <= x, 'ds-math-sub-underflow');
return z;
}
static mul(x: number, y: number) {
let z: number = x * y;
assert(y == 0 || z / y == x, 'ds-math-mul-overflow');
return z;
}
static div(x: number, y: number) {
let z: number = x / y;
assert(x > 0 || y > 0, 'ds-math-div-by-zero');
return z;
}
}
console.log(SafeMath.add(2,2)); // 4
console.log(SafeMath.sub(2,2)); // 0
console.log(SafeMath.mul(2,2)); // 4
console.log(SafeMath.div(2,2)); // 1
我的目标是让这些功能像这样工作,例如:
let balance0: number = 1;
let balance1: number = 1;
let amount0In: number = 10;
let amount1In: number = 10;
let balance0Adjusted: number = balance0.mul(1000).sub(amount0In.mul(3));
let balance1Adjusted: number = balance1.mul(1000).sub(amount1In.mul(3));
...函数将接受 y
并将前一个数字用作 x
。
您可以为此制作一些包装器:
if (!Number.prototype.mul) // check that the mul method does not already exist
{
Number.prototype.mul = function(n){ return this * n }
}
if (!Number.prototype.add)
{
Number.prototype.add = function(n){ return this + n }
}
let val = 5
let doubleValPlus500 = val.mul(2).add(500)
console.log( doubleValPlus500 )
Number.prototype 基于示例
import { assert } from "https://deno.land/std@0.102.0/testing/asserts.ts";
declare global {
/*
Augument global Number.prototype with the following custom functions
Warning - While this may look like a clean approach, it is considered
unsafe due to javascript possibly choosing to natively implement these
exact function names in the near future. To avoid this, choose unique
function names.
*/
interface Number {
add(n: number): number;
sub(n: number): number;
mul(n: number): number;
div(n: number): number;
pow(n: number): number;
sqrt(): number;
print(): number;
}
}
Number.prototype.add = function(this:number, n:number) {
assert((this + n) >= this, 'ds-math-add-overflow');
return this + n;
}
Number.prototype.sub = function(this:number, n:number) {
assert((this - n) <= this, 'ds-math-sub-underflow');
return this - n;
}
Number.prototype.mul = function(this:number, n:number) {
assert(n == 0 || (this * n) / n == this, 'ds-math-mul-overflow');
return this * n;
}
Number.prototype.div = function(this:number, n:number) {
assert(this > 0 || n > 0, 'ds-math-div-by-zero');
return this / n;
}
Number.prototype.pow = function(this:number, n:number) {
assert(this > 0 && n >= 2, 'ds-math-exp-to-zero');
return this ** n;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
Number.prototype.sqrt = function(this:number) {
assert(this > 0, 'ds-math-sqrt-of-zero');
let x: number = 0;
let y: number = this;
let z: number = 0;
if (y > 3) {
z = y;
x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
return z;
}
Number.prototype.print = function(this:number) {
console.log('=', this);
return this;
}
// Here it is in action:
let balance = 0;
balance.add(10).print().sub(1).print().mul(2).print().div(3).print().pow(4).print().sqrt().print();
输出:
= 10
= 9
= 18
= 6
= 1296
= 36
Class 基于示例
import { assert } from "https://deno.land/std@0.102.0/testing/asserts.ts";
class SafeMath {
private n: number;
constructor(start: number = 0) {
this.n = start;
}
public add(y: number) {
assert(this.n + y >= this.n, 'ds-math-add-overflow');
let z: number = this.n + y;
this.n = this.n + y;
return this;
}
public sub(y: number) {
assert(this.n - y <= this.n, 'ds-math-sub-underflow');
let z: number = this.n - y;
this.n = this.n - y;
return this;
}
public mul(y: number) {
assert(y == 0 || (this.n * y) / y == this.n, 'ds-math-mul-overflow');
let z: number = this.n * y;
this.n = this.n * y;
return this;
}
public div(y: number) {
assert(this.n > 0 || y > 0, 'ds-math-div-by-zero');
let z: number = this.n / y;
this.n = this.n / y;
return this;
}
public pow(y: number) {
assert(this.n > 0 && y >= 2, 'ds-math-exp-to-zero');
let z: number = this.n ** y;
this.n = this.n ** y;
return this;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
public sqrt() {
assert(this.n > 0, 'ds-math-sqrt-of-zero');
let x: number = 0;
let y: number = this.n;
let z: number = 0;
if (y > 3) {
z = y;
this.n = z;
x = y / 2 + 1;
while (x < z) {
z = x;
this.n = z
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
this.n = z
}
return this;
}
public print() {
console.log('=', this.n);
return this;
}
}
// Here it is in action:
new SafeMath(0).add(10).print().sub(1).print().mul(2).print().div(3).print().pow(4).print().sqrt().print();
输出:
= 10
= 9
= 18
= 6
= 1296
= 36
您可以修改 Number.prototype
以添加函数,以便您可以链接这些操作。使用 string
属性 键通常被认为是一种不好的做法(请参阅 Why is extending native objects a bad practice?). You can use unique symbol 属性 键而不是字符串 属性 键以避免名称冲突等
这是一个示例模块,它使用唯一符号安全地“扩展”Number.prototype
乘法函数,并将新函数签名添加到 TypeScript Number
接口:
mul.ts
const mul = Symbol("multiply");
function value(this: number, n: number) {
return this * n;
}
declare global {
interface Number {
[mul]: typeof value;
}
}
Object.defineProperty(Number.prototype, mul, { value });
export default mul;
在为减法、加法、除法等定义了上述模块后,您可以导入模块并使用它们导出的唯一符号进行链式运算:
import mul from "./mul.ts";
import sub from "./sub.ts";
const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance[mul](1000)[sub](amountIn[mul](3));
console.log(balanceAdjusted);
970
使这些数学运算可链接的一个好处是,您可以在处理空值时将它们与 optional chaining operator 结合使用,这有时会派上用场。
无需使用符号也可以完成同样的操作,但对于可能为 mul
等定义自己的 Number
方法的 JavaScript 的未来版本来说是不安全的:
mul.ts
function value(this: number, n: number) {
return this * n;
}
declare global {
interface Number {
mul: typeof value;
}
}
Object.defineProperty(Number.prototype, "mul", { value });
export {}; // you have to import or export something to make it a module
import "./mul.ts";
import "./sub.ts";
const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance.mul(1000).sub(amountIn.mul(3));
console.log(balanceAdjusted);
970
单独导入所有这些模块可能不是很方便,因此您也可以制作一个模块来组合所有其他模块:
math.ts
export { default as mul } from "./mul.ts";
export { default as sub } from "./sub.ts";
/* and so forth */
然后你可以导入它和select你想使用的:
import { mul, sub } from "./math.ts";
const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance[mul](1000)[sub](amountIn[mul](3));
console.log(balanceAdjusted);
我很难找到用于在线搜索此内容的关键字。
我创建了一个具有安全数学函数的 class。每个函数都有 2 个参数,在通过断言求值后,它 returns 结果。
示例:
class SafeMath {
static add(x: number, y: number) {
let z: number = x + y;
assert(z >= x, 'ds-math-add-overflow');
return z;
}
static sub(x: number, y: number) {
let z: number = x - y;
assert(z <= x, 'ds-math-sub-underflow');
return z;
}
static mul(x: number, y: number) {
let z: number = x * y;
assert(y == 0 || z / y == x, 'ds-math-mul-overflow');
return z;
}
static div(x: number, y: number) {
let z: number = x / y;
assert(x > 0 || y > 0, 'ds-math-div-by-zero');
return z;
}
}
console.log(SafeMath.add(2,2)); // 4
console.log(SafeMath.sub(2,2)); // 0
console.log(SafeMath.mul(2,2)); // 4
console.log(SafeMath.div(2,2)); // 1
我的目标是让这些功能像这样工作,例如:
let balance0: number = 1;
let balance1: number = 1;
let amount0In: number = 10;
let amount1In: number = 10;
let balance0Adjusted: number = balance0.mul(1000).sub(amount0In.mul(3));
let balance1Adjusted: number = balance1.mul(1000).sub(amount1In.mul(3));
...函数将接受 y
并将前一个数字用作 x
。
您可以为此制作一些包装器:
if (!Number.prototype.mul) // check that the mul method does not already exist
{
Number.prototype.mul = function(n){ return this * n }
}
if (!Number.prototype.add)
{
Number.prototype.add = function(n){ return this + n }
}
let val = 5
let doubleValPlus500 = val.mul(2).add(500)
console.log( doubleValPlus500 )
Number.prototype 基于示例
import { assert } from "https://deno.land/std@0.102.0/testing/asserts.ts";
declare global {
/*
Augument global Number.prototype with the following custom functions
Warning - While this may look like a clean approach, it is considered
unsafe due to javascript possibly choosing to natively implement these
exact function names in the near future. To avoid this, choose unique
function names.
*/
interface Number {
add(n: number): number;
sub(n: number): number;
mul(n: number): number;
div(n: number): number;
pow(n: number): number;
sqrt(): number;
print(): number;
}
}
Number.prototype.add = function(this:number, n:number) {
assert((this + n) >= this, 'ds-math-add-overflow');
return this + n;
}
Number.prototype.sub = function(this:number, n:number) {
assert((this - n) <= this, 'ds-math-sub-underflow');
return this - n;
}
Number.prototype.mul = function(this:number, n:number) {
assert(n == 0 || (this * n) / n == this, 'ds-math-mul-overflow');
return this * n;
}
Number.prototype.div = function(this:number, n:number) {
assert(this > 0 || n > 0, 'ds-math-div-by-zero');
return this / n;
}
Number.prototype.pow = function(this:number, n:number) {
assert(this > 0 && n >= 2, 'ds-math-exp-to-zero');
return this ** n;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
Number.prototype.sqrt = function(this:number) {
assert(this > 0, 'ds-math-sqrt-of-zero');
let x: number = 0;
let y: number = this;
let z: number = 0;
if (y > 3) {
z = y;
x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
return z;
}
Number.prototype.print = function(this:number) {
console.log('=', this);
return this;
}
// Here it is in action:
let balance = 0;
balance.add(10).print().sub(1).print().mul(2).print().div(3).print().pow(4).print().sqrt().print();
输出:
= 10
= 9
= 18
= 6
= 1296
= 36
Class 基于示例
import { assert } from "https://deno.land/std@0.102.0/testing/asserts.ts";
class SafeMath {
private n: number;
constructor(start: number = 0) {
this.n = start;
}
public add(y: number) {
assert(this.n + y >= this.n, 'ds-math-add-overflow');
let z: number = this.n + y;
this.n = this.n + y;
return this;
}
public sub(y: number) {
assert(this.n - y <= this.n, 'ds-math-sub-underflow');
let z: number = this.n - y;
this.n = this.n - y;
return this;
}
public mul(y: number) {
assert(y == 0 || (this.n * y) / y == this.n, 'ds-math-mul-overflow');
let z: number = this.n * y;
this.n = this.n * y;
return this;
}
public div(y: number) {
assert(this.n > 0 || y > 0, 'ds-math-div-by-zero');
let z: number = this.n / y;
this.n = this.n / y;
return this;
}
public pow(y: number) {
assert(this.n > 0 && y >= 2, 'ds-math-exp-to-zero');
let z: number = this.n ** y;
this.n = this.n ** y;
return this;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
public sqrt() {
assert(this.n > 0, 'ds-math-sqrt-of-zero');
let x: number = 0;
let y: number = this.n;
let z: number = 0;
if (y > 3) {
z = y;
this.n = z;
x = y / 2 + 1;
while (x < z) {
z = x;
this.n = z
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
this.n = z
}
return this;
}
public print() {
console.log('=', this.n);
return this;
}
}
// Here it is in action:
new SafeMath(0).add(10).print().sub(1).print().mul(2).print().div(3).print().pow(4).print().sqrt().print();
输出:
= 10
= 9
= 18
= 6
= 1296
= 36
您可以修改 Number.prototype
以添加函数,以便您可以链接这些操作。使用 string
属性 键通常被认为是一种不好的做法(请参阅 Why is extending native objects a bad practice?). You can use unique symbol 属性 键而不是字符串 属性 键以避免名称冲突等
这是一个示例模块,它使用唯一符号安全地“扩展”Number.prototype
乘法函数,并将新函数签名添加到 TypeScript Number
接口:
mul.ts
const mul = Symbol("multiply");
function value(this: number, n: number) {
return this * n;
}
declare global {
interface Number {
[mul]: typeof value;
}
}
Object.defineProperty(Number.prototype, mul, { value });
export default mul;
在为减法、加法、除法等定义了上述模块后,您可以导入模块并使用它们导出的唯一符号进行链式运算:
import mul from "./mul.ts";
import sub from "./sub.ts";
const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance[mul](1000)[sub](amountIn[mul](3));
console.log(balanceAdjusted);
970
使这些数学运算可链接的一个好处是,您可以在处理空值时将它们与 optional chaining operator 结合使用,这有时会派上用场。
无需使用符号也可以完成同样的操作,但对于可能为 mul
等定义自己的 Number
方法的 JavaScript 的未来版本来说是不安全的:
mul.ts
function value(this: number, n: number) {
return this * n;
}
declare global {
interface Number {
mul: typeof value;
}
}
Object.defineProperty(Number.prototype, "mul", { value });
export {}; // you have to import or export something to make it a module
import "./mul.ts";
import "./sub.ts";
const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance.mul(1000).sub(amountIn.mul(3));
console.log(balanceAdjusted);
970
单独导入所有这些模块可能不是很方便,因此您也可以制作一个模块来组合所有其他模块:
math.ts
export { default as mul } from "./mul.ts";
export { default as sub } from "./sub.ts";
/* and so forth */
然后你可以导入它和select你想使用的:
import { mul, sub } from "./math.ts";
const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance[mul](1000)[sub](amountIn[mul](3));
console.log(balanceAdjusted);