我正在学习函数式编程,我想知道什么是重构波纹管功能,查看最佳实践的最佳方法
我正在重构我制作的面向对象的粒子系统,所以这里首先是我作为类制作的代码:
submitToFields(fields) {
let totalAccelerationX = 0;
let totalAccelerationY = 0;
fields.forEach(field => {
const vectorX = field.position.x - this.position.x;
const vectorY = field.position.y - this.position.y;
const force = Particle.calculateForce(field, vectorX, vectorY);
totalAccelerationX += vectorX * force;
totalAccelerationY += vectorY * force;
});
this.acceleration = new Vector(totalAccelerationX, totalAccelerationY);
}
vector是{x:0,y:0}
该方法计算粒子受到粒子系统上的场(重力)影响的扰动加速度
好吧,这就是我到目前为止所取得的成就,但是我想对功能进行更多的了解,并尝试使功能编程可以为我提供更多的免费积分和其他好处。
const add = a => b => a + b;
const propX = R.prop('x');
const propY = R.prop('y');
const addVectors = (vectorA, vectorB) =>
vector(
add(propX(vectorA))(propX(vectorB)),
add(propY(vectorA))(propY(vectorB)),
);
const vector = (a, b) => ({ x: a, y: b });
const square = x => x ** 2;
const difference = a => b => a - b;
const position = item => R.prop('position', item) || 0;
const posX = item => R.prop('x', position(item)) || 0;
const posY = item => R.prop('y', position(item)) || 0;
const propMass = item => R.prop('mass', item) || 0;
const calculateForce = (mass, vectr) => {
return mass / (square(vectr.x) + square(vectr.y) + mass) ** 1.5;
};
const disturbanceAcceleration = (part, fields) => {
const partX = posX(part);
const partY = posY(part);
return fields.reduce((acc, curr) => {
const fieldX = difference(posX(curr))(partX);
const fieldY = difference(posY(curr))(partY);
const force = calculateForce(propMass(curr), vector(fieldX, fieldY));
const newVector = vector(fieldX * force, fieldY * force);
return addVectors(acc, newVector);
}, vector(0, 0));
};
这是一个测试:
给出:
const part = {position:{x:0,y:0}};
const fields = [
{position:{x:100,y:100}, mass:140},
{position:{x:200,y:100}, mass:140} ,
{position:{x:150,y:300}, mass:140}];
执行:
disturbanceAcceleration(part,fields)
输出:
{x: 0.007947635786319896, y: 0.007256173830876778}
答案 0 :(得分:4)
Vector.js
从基本的Vector
模块开始,我们使用对象来保存x
和y
属性,但要确保Vector操作add
,difference
,并且scale
不对对象进行变异;相反,总是返回一个新的Vector-
const Vector = (x = 0, y = 0) =>
({ x, y })
Vector.add = (a = Vector (), b = Vector ()) =>
Vector
( a.x + b.x
, a.y + b.y
)
Vector.difference = (a = Vector (), b = Vector ()) =>
Vector
( a.x - b.x
, a.y - b.y
)
Vector.scale = ({ x, y } = Vector (), k = 1) =>
Vector
( x * k
, y * k
)
// ...
export default Vector
Field.js
然后,我们构建一个Field
模块,该模块取决于Vector
。同样,我们确保不使用可变操作-
const Vector =
require ('./Vector')
const Field = (mass = 0, position = Vector ()) =>
({ mass, position })
Field.calculateForce = ({ mass, position:{ x, y } } = Field ()) =>
mass / (x ** 2 + y ** 2 + mass) ** 1.5
Field.disturbanceAcceleration = (origin, fields = []) =>
fields.reduce
( (acc, { mass, position }) =>
{ const v =
Vector.difference (position, origin)
const force =
Field.calculateForce (Field (mass, v))
return Vector.add
( acc
, Vector.scale (v, force)
)
}
, Vector ()
)
// ...
export default Field
现在为我们提供解决方案-
import Vector from './Vector'
import Field from './Field'
const fields =
[ Field (140, Vector (100, 100))
, Field (140, Vector (200, 100))
, Field (140, Vector (150, 300))
]
Field.disturbanceAcceleration (Vector (), fields)
// { x: 0.007947635786319896, y: 0.007256173830876778 }
使用默认参数使我们可以非常方便地使用数据类型。这些是我们不认为理所当然的事情-
Vector () // { x: 0, y: 0 }
Vector () .x // 0
Vector () .y // 0
Vector (1, 2) .x // 1
Vector (1, 2) .y // 2
Field () // { mass: 0, position: { x: 0, y: 0 } }
Field () .mass // 0
Field () .position // { x: 0, y: 0 }
Field (100) .mass // 100
Field (100) .position // { x: 0, y: 0 }
Field (100, Vector (1, 2)) .mass // 100
Field (100, Vector (1, 2)) .position .x // 1
Field (100, Vector (1, 2)) .position .y // 2
myparticle.js
您可能会得到许多子模块,这没关系。您可能希望将子模块组合成一个更大的模块,也许我们将其命名为 myparticle -
import Vector from './Vector'
import Field from './Field'
// ...
export { Vector, Field /*, ... */ }
然后,当我们在解决方案中使用它时-
import { Vector, Field /*, ... */ } from ('myparticle')
// ...
代码演示
展开以下代码段,以在您自己的浏览器中验证结果-
// Vector -------------------------------------------
const Vector = (x = 0, y = 0) =>
({ x, y })
Vector.add = (a = Vector (), b = Vector ()) =>
Vector
( a.x + b.x
, a.y + b.y
)
Vector.difference = (a = Vector (), b = Vector ()) =>
Vector
( a.x - b.x
, a.y - b.y
)
Vector.scale = ({ x, y } = Vector (), k = 1) =>
Vector
( x * k
, y * k
)
// Field -------------------------------------------
const Field = (mass = 0, position = Vector ()) =>
({ mass, position })
Field.calculateForce = ({ mass, position:{ x, y } } = Field ()) =>
mass / (x ** 2 + y ** 2 + mass) ** 1.5
Field.disturbanceAcceleration = (origin, fields = []) =>
fields.reduce
( (acc, { mass, position }) =>
{ const v =
Vector.difference (position, origin)
const force =
Field.calculateForce (Field (mass, v))
return Vector.add
( acc
, Vector.scale (v, force)
)
}
, Vector ()
)
// Demo -------------------------------------------
const fields =
[ Field (140, Vector (100, 100))
, Field (140, Vector (200, 100))
, Field (140, Vector (150, 300))
]
const result =
Field.disturbanceAcceleration (Vector (), fields)
console .log (result)
// { x: 0.007947635786319896, y: 0.007256173830876778 }
普通对象模块
在上面我们制作了一个函数 Vector
,然后附加add
,difference
和scale
作为函数的属性。这在JavaScript中有效,因为函数是对象,我们可以选择任何方式分配属性。这种设计使我们可以使用Vector
作为构造函数来构造矢量,然后调用Vector.add
,Vector.scale
等进行与矢量相关的操作。
另一种设计是使Vector
成为普通对象。可以将向量运算add
,difference
和scale
添加为对象的属性。然后可以将构造函数添加为属性make
或您选择的任何名称-
// Vector.js
const make = (x = 0, y = 0) =>
({ x, y })
const add = ...
const difference = ...
const scale = ...
export { make, add, difference, scale }
Vector
不再是一个功能。而是将构造函数导出为make
。现在我们使用Vector.make
-
import Vector from './Vector'
const u =
Vector.make (1, 1) // must call Vector.make
const v =
Vector.make (2, 3) // must call Vector.make
Vector.add (u, v) // { x: 3, y: 4 }
无论您决定采用哪种方式打包模块,只要确保其一致性即可。