【问题标题】:Functional Programming Exercise函数式编程练习
【发布时间】:2015-09-13 19:29:53
【问题描述】:

作为一个函数式编程练习,我想我会编写一个小程序来按盈利能力对 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) 

然后对列表进行排序?感觉就像我错过了一些东西 - 仍然在 OO 中思考,更不用说以这种方式添加新配方会相当尴尬。

有任何提示可以更好地使用函数概念吗?看起来这种类型的计算问题很适合函数式。

非常感谢。

【问题讨论】:

    标签: f# functional-programming


    【解决方案1】:

    这是一个相当复杂的问题,有多种可能的答案。此外,很难猜测您的域的任何信息(我不知道您在玩什么游戏:-)),所以我会尝试根据示例进行一些补充。

    基本的功能方法是使用可区分的联合对不同的配方进行建模。

    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,并需要您想要评估的配方。

    这个例子应该会给你一些想法——从一个有区别的联合开始对问题域(不同的配方)建模,然后在其中编写一个带有模式匹配的函数通常是一个很好的开始方式——但很难说更多您问题中的信息有限。

    【讨论】:

    • 到目前为止我尝试过的方法肯定缺乏定义问题域的好方法。但是不确定我是否遵循如何使用匹配功能,或gs 的作用。 FancySword 的 lambda 中是否已经存在数量(2.015.0)和价格(prices.Goldprices.Steel)?游戏是刀锋与灵魂,顺便说一句:)
    • 是的,我对这个问题的理解有点有限:-)。我的想法是,有一些配方参数(你需要一些青蛙腿来制造魔法石),然后你在游戏中有一些全球价值的金币(来自Prices)。如果成本不取决于外部价格,那么您可以将所有信息放在 Recipe 案例中 - 您的示例中可能就是这种情况。
    【解决方案2】:

    在函数式语言中,您只能使用函数来做任何事情。在这里您可以定义常见的盈利功能并使用它和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
    

    【讨论】:

      【解决方案3】:

      我认为公认的答案有点缺乏类型安全性 - 你已经说过 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&lt;GameMoney&gt;

      既然你提到了体积,我想你可以看到如何复制相同的模式来编写类型安全的函数,将总配方体积计算为 int&lt;InventoryVolume&gt; 类型的值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-22
        • 2011-11-01
        • 2011-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-27
        相关资源
        最近更新 更多