2015-09-13 104 views
0

作为函数式编程练习,我认为我会编写一个小程序,以毫米盈利的方式对制作食谱进行排序。函数式编程练习

在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'的作用。 FancySword的lambda中是否已经存在数量('2.0','15.0')和价格('prices.Gold','prices.Steel')?游戏是刀锋和灵魂,顺便说一句:) – Osan

+0

是的,我对这个问题的理解是有限的:-)。我的想法是这个配方有一些参数(你需要一定数量的青蛙腿来创造魔法石),然后你在游戏中拥有一些全球黄金价值(来自'价格')。如果成本不依赖于外部价格,那么你可以把所有的信息放在'Recipe'的情况下 - 这可能就是你的例子。 –

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>类型的值。