2013-09-04 5 views
8

मज़े के लिए point-free style जावास्क्रिप्ट के साथ खेलना।यह जावास्क्रिप्ट में कार्यात्मक लेंस के लिए एक जगह है?

मैं वीडियो गेम डियाब्लो कोडिंग कर रहा हूँ, और मैं इस लेकिन गहरी और अधिक जटिल की तरह जटिल नेस्टेड प्रकार का उपयोग दुश्मनों को मॉडलिंग कर रहा हूँ कहते हैं:

{ name: "badguy1", stats: { health: 10: strength: 42 }, pos: {x: 100, y: 101 } } 

तो मैं अपने सभी दुश्मनों की एक सूची है। मैं किसी विशिष्ट दायरे

function isInRange(radius, point) { return point.x^2 + point.y^2 >= radius^2; } 
function fireDamage(health) { return health - 10; }  
var newEnemies = enemies.filter(isInRange).map(fireDamage); 

पाठ्यक्रम के इस प्रकार नहीं है जाँच के भीतर सभी दुश्मनों को नुकसान करना चाहते हैं - मेरे combinators पुरातन लेते हैं, तो मैं नक्शा और "नीचे एक अन्य स्तर पर" फिल्टर करने के लिए की जरूरत है। मैं फ़िल्टर/मानचित्र व्यापार तर्क पाइपलाइन अस्पष्ट नहीं करना चाहता हूं। I know lenses can help me लेकिन मान लें कि मैं एक ब्राउज़र में हूं, क्योंकि यह निश्चित रूप से परिवर्तनीय संरचनाओं के साथ छोटा है। मैं यह कैसे करुं?

उत्तर

6

my article on lenses पढ़ें। यह आपके प्रश्न का सही जवाब देता है जिस तरह से आपने इसे शब्द दिया था। गंभीरता से, मैं मजाक भी नहीं कर रहा हूँ। यहां मेरी पोस्ट से एक कोड स्निपेट है:

fireBreath :: Point -> StateT Game IO() 
fireBreath target = do 
    lift $ putStrLn "*rawr*" 
    units.traversed.(around target 1.0).health -= 3 
+0

@DustinGetz तो क्या आपका मतलब है कि आप इसे जावास्क्रिप्ट में अनुवाद करना चाहते हैं? –

+1

@DustinGetz क्या आप इसे 'लेंस' लाइब्रेरी (यानी कार्यों के कार्यों) की शैली में बिल्कुल करने की कोशिश कर रहे हैं या क्या आप केवल प्रथम श्रेणी के गेटर्स और सेटर्स बना रहे हैं? –

+0

मेरे मूल्य अधिक गहराई से घोंसले हैं, लेकिन ग्राफ नहीं। 4 की गहराई कहो। मुझे लगता है कि कार्यों के कार्यों की आवश्यकता है। –

5

क्या आपका प्रश्न जावास्क्रिप्ट में लेंस का उपयोग करने के बारे में है? यदि ऐसा है, तो मैं मदद करने में सक्षम हो सकता है। क्या आपने Ramda.js library की जांच की है? यह कार्यात्मक जेएस लिखने का एक शानदार तरीका है। के अपने दुश्मन मॉडल को देखकर शुरू करते हैं:

/* -- data model -- */ 
let enemyModel = { 
    name: "badguy1", 
    stats: { 
    health: 10, 
    strength: 42 
    }, 
    pos: { 
    x: 100, 
    y: 101 
    } 
}; 

लेंस: आदेश में एक लेंस आप एक गेटर विधि और अपने विशिष्ट वस्तु के लिए एक सेटर विधि की जरूरत का निर्माण करने में - "दुश्मन" में अपने मामले। यहां बताया गया है कि आप उन्हें हाथ से कैसे बना सकते हैं।

विधि 1: वस्तुओं

const healthLens = lensPath(['stats', 'health']); 

एक बार जब आप लेंस बना लिया है के लिए Ramda के समीचीन सुविधा लेंस है, यह है: अपनी खुद की getters और setters

const getHealth = path(['stats', 'health']); 
const setHealth = assocPath(['stats', 'health']); 
const healthLens = lens(getHealth, setHealth); 

विधि 2 बनाएं इसका इस्तेमाल करने का समय रामदा लेंस का उपयोग करने के लिए 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(..) का उपयोग करना चाहेंगे। इसके अलावा, चूंकि आपकी स्थिति निर्देशांक दुश्मन मॉडल में घिरे हुए हैं, इसलिए आप उन लोगों तक पहुंचने के लिए एक लेंस का उपयोग करना चाहेंगे। चलिए एक बनाते हैं और isInRange(..) रिफैक्टर करते हैं जबकि हम इसमें हैं।

/* -- 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); 
:

/* -- 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 का एक संग्रह के साथ काम कर कैसा लगेगा क्या करना है:

// 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(..) 
} 

यहाँ एक कार्यात्मक दृष्टिकोण है:

एक संदर्भ के रूप में, यहाँ मूल fn है

मुझे आशा है कि इससे यह पता चलता है कि उपयोगी लेंस कितने उपयोगी हो सकते हैं।हालांकि कई सहायक कार्य हैं, मुझे लगता है कि कोड का अंतिम टुकड़ा सुपर अर्थपूर्ण है!

आखिरकार, मैं इस उदाहरण को अधिक पढ़ने योग्य बनाने के लिए रामदा से इन कार्यों के साथ अपने दायरे में बाढ़ कर रहा हूं। मैं इसे पूरा करने के लिए ES6 deconstruction का उपयोग कर रहा हूँ। यहां बताया गया है कि:

const { 
    add, 
    assocPath, 
    compose, 
    converge, 
    curry, 
    filter, 
    flip, 
    gte, 
    lens, 
    lensPath, 
    map, 
    over, 
    set, 
    path, 
    prop, 
    view 
} = R; 

// code goes below... 

जेएसबीन में इसे आज़माएं! वे रामदा समर्थन प्रदान करते हैं।

संबंधित मुद्दे