あなたの質問は、Javascript でレンズを使用する方法についてですか? もしそうなら、私は助けることができるかもしれません。Ramda.js ライブラリをチェックアウトしましたか? これは関数型 JS を作成する素晴らしい方法です。まず、敵のモデルを見てみましょう。
/* -- data model -- */
let enemyModel = {
name: "badguy1",
stats: {
health: 10,
strength: 42
},
pos: {
x: 100,
y: 101
}
};
レンズ:レンズを作成するには、特定のオブジェクト (この場合は「敵」) のゲッター メソッドとセッター メソッドが必要です。これらを手動で作成する方法は次のとおりです。
方法 1:独自のゲッターとセッターを作成する
const getHealth = path(['stats', 'health']);
const setHealth = assocPath(['stats', 'health']);
const healthLens = lens(getHealth, setHealth);
方法 2:オブジェクトに対する Ramda の都合のよい便利なレンズ
const healthLens = lensPath(['stats', 'health']);
レンズを作成したら、それを使用します。Ramda は、レンズを使用するための 3 つの関数を提供します: view(..)
、set(..)
、およびover(..)
。
view(healthLens)(enemyModel); // 10
set(healthLens, 15)(enemyModel); // changes health from 10 to 15
over(healthLens, fireDamage)(enemyModel); // reduces enemyModel's health property by 10
この機能を敵の体力に適用しているのでfireDamage(..)
、 を使用する必要がありますover(..)
。また、位置座標はenemyModel内にネストされているため、レンズを使用してそれらにアクセスすることもできます。それを作成して、その間にリファクタリングisInRange(..)
しましょう。
参考までに、元の fn は次のとおりです。
// NOTE: not sure if this works as you intended it to...
function isInRange(radius, point) {
return point.x^2 + point.y^2 >= radius^2; // maybe try Math.pow(..)
}
機能的なアプローチは次のとおりです。
/* -- helper functions -- */
const square = x => x * x;
const gteRadSquared = radius => flip(gte)(square(radius));
let sumPointSquared = point => converge(
add,
[compose(square, prop('x')),
compose(square, prop('y'))]
)(point);
sumPointSquared = curry(sumPointSquared); // allows for "partial application" of fn arguments
/* -- refactored fn -- */
let isInRange = (radius, point) => compose(
gteRadSquared(radius),
sumPointSquared
)(point);
isInRange = curry(isInRange);
これは、enemyModels のコレクションを扱う場合にどのようになるかを示しています。
/* -- lenses -- */
const xLens = lensPath(['pos', 'x']);
const yLens = lensPath(['pos', 'y']);
const ptLens = lens(prop('pos'), assoc('pos'));
// since idk where 'radius' is coming from I'll hard-code it
let radius = 12;
const filterInRange = rad => filter(
over(ptLens, isInRange(rad)) // using 'ptLens' bc isInRange(..) takes 'radius' and a 'point'
);
const mapFireDamage = map(
over(healthLens, fireDamage) // using 'healthLens' bc fireDamage(..) takes 'health'
);
let newEnemies = compose(
mapFireDamage,
filterInRange(radius)
)(enemies);
これが、レンズがいかに役立つかを説明するのに役立つことを願っています. 多くのヘルパー関数がありますが、コードの最後の部分は非常にセマンティックだと思います!
最後に、この例を読みやすくするために、Ramda のこれらの関数でスコープをあふれさせています。これを達成するためにES6の分解を使用しています。方法は次のとおりです。
const {
add,
assocPath,
compose,
converge,
curry,
filter,
flip,
gte,
lens,
lensPath,
map,
over,
set,
path,
prop,
view
} = R;
// code goes below...
jsBin で試してみてください。Ramda サポートを提供します。