2015-09-13 2 views
0

Как упражнение по функциональному программированию, я думал, что напишу небольшую программу для ранжирования рецептов изготовления в mmo по прибыльности.Упражнение функционального программирования

В языке OO я бы создал объекты стратегии для каждого рецепта, в качестве членов которого были Cost(), ExpectedRevenue() и Volume(). Затем я помещал все объекты в список и сортировал их с помощью функции доходности/времени.

Попытка выполнить тот же результат в F #, но я не уверен, как это сделать. У меня есть некоторые разрозненные функции затрат, например:

let cPM (ss,marble) = (15.0 * ss + 10.0 * marble + 0.031)/5.0 
let cTRef (tear,clay) = (tear + 10.0 * clay + 0.031)/5.0 

, а затем доходы и объем определения как:

let rPM = 1.05 
let vPM = 50 

, но я не уверен, что теперь делать. Составьте список кортежей, которые выглядят так:

(name: string, cost:double, revenue:double, volume:int) 

, а затем отсортировать список? Мне кажется, что я что-то упускаю - все еще думая в ОО, не говоря уже о добавлении новых рецептов таким образом, будет довольно неудобно.

Есть ли у кого-нибудь советы по использованию функциональных концепций лучше? Казалось, что этот тип проблем с расчетом будет подходящим для функционального стиля.

Большое значение.

ответ

3

Это довольно сложный вопрос с несколькими возможными ответами. Кроме того, довольно сложно угадать что-нибудь о вашем домене (я не знаю, в какую игру вы играете :-)), поэтому я попытаюсь что-то сделать, основываясь на этом примере.

Основным функциональным подходом было бы моделирование различных рецептов с использованием дискриминационного союза.

type Recipe = 
    | FancySword of gold:float * steel:float // Sword can be created from gold & steel 
    | MagicalStone of frogLegs:float // Magical stone requires some number of frog legs 

Кроме того, мы должны знать цены на вещи в игре:

type Prices = { Gold : float; Steel : float; FrogLegs : float } 

Теперь вы можете писать функции для расчета стоимости и ожидаемый доход от рецептов:

let cost prices recipe = 
    match recipe with 
    | FancySword(g, s) -> 
     // To create a sword, we need 2 pieces of gold and 15 pieces of steel 
     2.0 * g * prices.Gold + s * 15.0 * prices.Steel 
    | MagicalStone(l) -> l * prices.FrogLeg 

Это берет запись со всеми prices и принимает рецепт, который вы хотите оценить.

Пример должен дать вам некоторую идею - начиная с дискриминированного объединения, чтобы моделировать проблемную область (разные рецепты), а затем написать функцию с совпадением шаблонов в ней, как правило, это хороший способ начать работу - но трудно сказать больше с ограниченной информацией в вашем вопросе.

+0

Хороший способ определить проблемную область, безусловно, отсутствовал в том, что я пробовал до сих пор. Не уверен, что я следую, как использовать функцию соответствия, или роль 'g' и' s'. Не количество ('2.0',' 15.0') и цены ('prices.Gold',' prices.Steel') уже присутствуют в лямбда для FancySword? Игра Blade and Soul, btw :) – Osan

+0

Да, мое понимание проблемы немного ограничено :-). Моя идея состоит в том, что есть некоторые параметры рецепта (вам нужно некоторое количество ног лягушек для создания магического камня), а затем у вас есть глобальная ценность золота в игре (что происходит от «Цены»). Если стоимость не зависит от внешних цен, то вы можете поместить всю информацию в случаях «Рецепт» - это, вероятно, в вашем примере. –

1

В функциональных языках вы можете делать что угодно только с функциями. Здесь вы можете определить общую функцию прибыльности и сортировать рецепты с ним и List.sortBy:

// recipe type with constants for Revenue, Volume and (ss,marble) 
type recipe = {r: float; v: float; smth: float * float} 

// list of recipes 
let recipes = [ 
    {r = 1.08; v = 47.0; smth = (28.0, 97.0)}; 
    {r = 1.05; v = 50.0; smth = (34.0, 56.0)} ] 

// cost function 
let cPM (ss,marble) = (15.0 * ss + 10.0 * marble + 0.031)/5.0 

// profitability function with custom coefficients 
let profitability recipe = recipe.r * 2.0 + recipe.v * 3.0 + cPM recipe.smth 

// sort recipes by profitability 
let sortedRecipes = 
    List.sortBy profitability recipes 

// note: it's reordered now 
printfn "%A" sortedRecipes 
1

Принятая ответ немного не хватает в безопасности типа, я думаю - вы уже говорили, что FancySword сделан из золота и стали, поэтому вам не нужно забывать правильно сочетать золото с ценой на золото! Система типов должна проверить это для вас и предотвратить случайную ошибку g * prices.Steel.

Если набор возможных типов ресурсов исправлен, то это хороший прецедент для единиц измерения.

[<Measure>] type Gold 
[<Measure>] type Steel 
[<Measure>] type FrogLegs 
[<Measure>] type GameMoney 

type Recipe = { 
       goldQty  : float<Gold> 
       steelQty  : float<Steel> 
       frogLegsQty : int<FrogLegs> 
       } 

type Prices = { 
       goldPrice  : float<GameMoney/Gold> 
       steelPrice : float<GameMoney/Steel> 
       frogLegsPrice : float<GameMoney/FrogLegs> 
       } 

let recipeCost prices recipe = 
    prices.goldPrice  * recipe.goldQty   + 
    prices.steelPrice  * recipe.steelQty  + 
    // frog legs must be converted to float while preserving UoM 
    prices.frogLegsPrice * (recipe.frogLegsQty |> float |> LanguagePrimitives.FloatWithMeasure) 

let currentPrices = {goldPrice = 100.0<GameMoney/Gold>; steelPrice = 50.0<GameMoney/Steel>; frogLegsPrice = 2.5<GameMoney/FrogLegs> } 

let currentCost = recipeCost currentPrices  

let fancySwordRecipe = {goldQty = 25.4<Gold>; steelQty = 76.4<Steel>; frogLegsQty = 0<FrogLegs>} 

let fancySwordCost = currentCost fancySwordRecipe 

Компилятор теперь обеспечит выполнение всех расчетов. Например, в функции recipeCost он гарантирует, что сумма равна float<GameMoney>.

Поскольку вы упомянули том, я думаю, вы можете увидеть, как вы можете реплицировать один и тот же шаблон для написания безопасных по типу функций, которые будут вычислять общие объемы рецептов как значение типа int<InventoryVolume>.

Смежные вопросы