2013-09-04 21 views
8

Eğlence için point-free style javascript ile oynamak. javascript'te fonksiyonel lensler için bir nokta mı?

Ben video oyunu Diablo kodlama ediyorum ve bu ancak daha derin ve daha karmaşık gibi karmaşık iç içe türleri kullanarak düşmanları modelleme am Say:
{ name: "badguy1", stats: { health: 10: strength: 42 }, pos: {x: 100, y: 101 } } 

Bu yüzden bütün düşmanlarımın bir listesi var. Benim combinators ilkeller almak, bu yüzden harita ve "aşağı bir seviye" filtresine gerek - Ben belirli bir yarıçap

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); 

elbette bu kontrol yazın etmez içindeki tüm düşmanlara hasar yapmak istiyorum. Filtre/harita iş mantığı boru hattını gizlemek istemiyorum. I know lenses can help me ama bir tarayıcıda olduğumu söyleyeyim, çünkü bu elbette değişebilir yapılar ile önemsiz. Nasıl yaparım?

cevap

6

my article on lenses. Sorunuzu tam olarak ifade ettiğiniz şekilde cevaplar. Cidden, şaka yapmıyorum bile.

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

@DustinGetz Yani bunu Javascript'e çevirmek istediğiniz anlamına mı geliyorsunuz? –

+1

@DustinGetz Bunu tam olarak 'objektif' kütüphanesi tarzında (yani fonksiyonların işlevleri) yapmaya mı çalışıyorsunuz yoksa sadece birinci sınıf alıcılar ve ayarlayıcılar mı yapıyorsunuz? –

+0

Değerlerim daha derin iç içe, ancak bir grafik değil. Derinlik 4 deyin. Fonksiyonların fonksiyonlarının gerekli olduğunu düşünüyorum. –

5

soru JavaScript lensleri kullanma hakkında edilir mi: İşte benim görevinden bir kod parçacığı var? Eğer öyleyse, yardımcı olabilirim. Ramda.js library'u kontrol ettin mi? Fonksiyonel JS yazmanın müthiş bir yolu. en düşman modeline bakarak başlayalım:

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

Objektif: Bir alıcı yöntemini ve spesifik nesne için bir ayarlayıcı yöntemi gereken bir lens inşa etmek amacıyla - "düşman" senin durumda. Bunları el ile nasıl inşa edebilirsiniz.

Yöntem 1: Nesneler

Eğer lens oluşturduktan sonra
const healthLens = lensPath(['stats', 'health']); 

için Ramda en uygun rahatlık-lens, bu kadar: Kendi Alıcılar ve ayarlayıcılar

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

Yöntem 2 oluştur kullanmanın zamanı. Ramda, lens kullanmak için 3 işlev sunar: view(..), set(..) ve 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 

düşmanın sağlığına fireDamage(..) fonksiyonunu uyguluyorsanız olduğundan, over(..) kullanmak isteyeceksiniz. Ayrıca, konum koordinatlarınız düşmanModel içinde yuvalandığından, bunlara erişmek için bir lens kullanmak isteyeceksiniz. Biz bir tane oluşturduk ve biz var iken isInRange(..) refactor.

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

İşte
/* -- 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); 

o enemyModels bir koleksiyon ile uğraşırken gibi görünecektir budur:

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

İşte fonksiyonel bir yaklaşım: Bir referans olarak

, burada kökeni fn var

Umarım bu, yararlı lenslerin nasıl olabileceğini gösterir.Çok sayıda yardımcı fonksiyon olsa da, son kod parçasının süper anlamlıdır!

Son olarak, bu örneği daha okunaklı hale getirmek için kapsamımı Ramda'nın bu işlevleriyle dolduruyorum. Bunu gerçekleştirmek için ES6 yapılışı kullanıyorum. İşte nasıl:

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

// code goes below... 

jsBin'de deneyin! Ramda desteği sunuyorlar.

İlgili konular