2017-04-04 72 views
1

更新,请参阅下面

我目前在学习Elm,这是我的第一个函数式语言。 我在我的模型中有List ResourceList Converter。 A Converter需要List Resource并输出另一个List Resource。转换仅在有足够的输入时才会发生。然后我想遍历每个Converter并减少/增加资源数量。在列表的每次迭代中更新模型

我无法把头围绕解决方案。我有一种感觉,List.foldr是我应该使用,但我不知道。我应该如何更新模型以便每次迭代都会更新?

这是我到目前为止有:

type alias Resource = 
{ resourcetype : ResourceType 
, quantity: Int 
} 

type ResourceType 
    = Energy 
    | Water 
    | Metal 

type alias Converter = 
    { convertertype : ConverterType 
    , intakes : List (ResourceType, Int) 
    , outputs: List (ResourceType, Int) 
    } 

type ConverterType 
    = SolarCollector 
    | Humidifer 
    | MetalCollector 

type alias Model = 
    { resources : List Resource 
    , converters : List Converter 
    } 

type Msg 
    = Tick Time 

update : Msg -> Model -> (Model, Cmd Msg) 
update msg model = 
    case msg of 
     Tick time -> 
      (updateGame model, Cmd.none) 

convert : List Resource -> Converter -> List Resource 
convert resources converter = 
    let 
     intakes = converter.intakes 
     outputs = converter.outputs 
     -- TODO, remove intakes from resources and add outputs to resources 
    in 
     resources 

updateGame : Model -> Model 
updateGame model = 
    -- TODO, call convert on each converter and update model 
    model 

subscriptions : Model -> Sub Msg 
subscriptions model = 
    Time.every second Tick 

例子:

资源不会耗尽:

--Initial converters 
[ SolarCollector [] [Energy 2] 
, MetalCollector [Energy 1] [Metal 1] 
] 
--Initial resources 
[ Energy 10, Metal 0] 

--After 5 ticks 
[ Energy 15, Metal 5] 

资源将耗​​尽:

--Initial converters 
[ MetalCollector [Energy 2] [Metal 1] 
, SolarCollector [] [Energy 1] 
] 
--Initial resources 
[ Energy 4, Metal 0] 

--Tick 1 
[ Energy 3, Metal 1] -- MetalCollector uses 2 Energy to get 1 Metal, SolarCollector will generate 1 Energy 
--Tick 2 
[ Energy 2, Metal 2] -- MC -2E,+1M; SC +1E 
--Tick 3 
[ Energy 1, Metal 3] -- MC -2E,+1M; SC +1E 
--Tick 4 
[ Energy 2, Metal 3] -- SC +1E 
-- Notice how this tick the MetalCollector didn't run because of list order. 
--Tick 5 
[ Energy 1, Metal 4] -- MC -2E,+1M; SC +1E 

更新:

我明白了!现在,Converter的顺序非常重要,它需要在每个订单上使用适量的资源。这是最后的工作代码,谢谢你帮助我!在这里,你可以试一下:https://ellie-app.com/RB3YsxwbGja1/0

module Main exposing (..) 

import Html exposing (Html, div, text, program, ul, li) 
import Time exposing (Time, second) 

type alias Resources = 
    { energy : Float 
    , water : Float 
    , metal : Float 
    } 

resourcesToList : Resources -> List Float 
resourcesToList resources = 
    [resources.energy, resources.water, resources.metal] 

noResource : Resources 
noResource = Resources 0 0 0 

energyResource : Float -> Resources 
energyResource energy = 
    { noResource | energy = energy } 

waterResource : Float -> Resources 
waterResource water = 
    { noResource | water = water } 

metalResource : Float -> Resources 
metalResource metal = 
    { noResource | metal = metal } 

type alias Converter = 
    { convertertype : ConverterType 
    , quantity : Int 
    , intakes : Resources 
    , outputs: Resources 
    } 

type ConverterType 
    = SolarCollector 
    | Humidifer 
    | MetalCollector 

initialResources : Resources 
initialResources = 
    { noResource | energy = 10} 

initialConverters : List Converter 
initialConverters = 
    [ { convertertype = MetalCollector 
    , quantity = 2 
    , intakes = energyResource 1 
    , outputs = metalResource 1 
    } 
    , { convertertype = SolarCollector 
    , quantity = 2 
    , intakes = noResource 
    , outputs = energyResource 1 
    } 
    , { convertertype = Humidifer 
    , quantity = 1 
    , intakes = energyResource 1 
    , outputs = waterResource 1 
    } 
    ] 

convert : Converter -> Resources -> Resources 
convert converter resources = 
    let 
     activatedQuantity = 
      toFloat (getActiveConverterQuantity converter resources) 

     getActiveConverterQuantity : Converter -> Resources -> Int 
     getActiveConverterQuantity converter resources = 
      let 
       resourcesList = resourcesToList resources 
       intakesList = resourcesToList converter.intakes 

       finalList = 
        List.map2 (,) resourcesList intakesList 
         |> List.filter (\(r,i) -> i > 0) 
         |> List.map (\(r,i) -> floor (r/i)) 
      in 
       case List.maximum finalList of 
        Just q -> 
         min q converter.quantity 
        Nothing -> 
         converter.quantity 

     subtractIntakes : Converter -> Resources -> Resources 
     subtractIntakes converter resources = 
      { resources 
      | energy = resources.energy - activatedQuantity * converter.intakes.energy 
      , water = resources.water - activatedQuantity * converter.intakes.water 
      , metal = resources.metal - activatedQuantity * converter.intakes.metal 
      } 
     addOutputs : Converter -> Resources -> Resources 
     addOutputs converter resources = 
      { resources 
      | energy = resources.energy + activatedQuantity * converter.outputs.energy 
      , water = resources.water + activatedQuantity * converter.outputs.water 
      , metal = resources.metal + activatedQuantity * converter.outputs.metal 
      } 
    in 
     resources 
      |> subtractIntakes converter 
      |> addOutputs converter 
-- MODEL 


type alias Model = 
    { resources : Resources 
    , converters : List Converter 
    } 


init : (Model, Cmd Msg) 
init = 
    ({ resources = initialResources 
    , converters = initialConverters 
    } 
    , Cmd.none 
    ) 



-- MESSAGES 


type Msg 
    = Tick Time 

-- VIEW 


view : Model -> Html Msg 
view model = 
    div [] 
     [ div[] [text (toString model.resources)] 
     , div[] 
      [ model.converters 
       |> List.map 
        (\c -> li [] [text (toString (c.convertertype,c.quantity,c.intakes))]) 
       |> ul [] 
      ] 
     ] 

-- UPDATE 


update : Msg -> Model -> (Model, Cmd Msg) 
update msg model = 
    case msg of 
     Tick time -> 
      (updateGame model, Cmd.none) 

updateGame : Model -> Model 
updateGame model = 
    let 
     newResources = model.converters |> List.foldr convert model.resources 
    in 
     { model | resources = newResources } 


-- SUBSCRIPTIONS 


subscriptions : Model -> Sub Msg 
subscriptions model = 
    Time.every second Tick 

-- MAIN 


main : Program Never Model Msg 
main = 
    program 
     { init = init 
     , view = view 
     , update = update 
     , subscriptions = subscriptions 
     } 
+0

能否请添加输入数据和期望的输出的一个例子? – halfzebra

+0

我添加了一些示例来涵盖用例。 –

+0

在你的情况'List.map'将比'List.foldr'更有帮助,但更好的是从'List'切换到'Dict'。 – daniula

回答

1

我期望你的model.resources不会有持有一个的ResourceType的多个值这么一个记录会使事情变得更容易为( - >假设资源= [能源2,水1,节能2]将毫无意义)

type alias Resources = 
    { energy : Int 
    , water : Int 
    , metal : Int 
    } 

type alias Model = 
    { resources : Resources 
    , converters : List Converter 
    } 

现在你可以折叠每个转换器的进气口和积累的资源

convert : Converter -> Resources -> Resources 
convert {intakes, outputs} resources = 
    let 
     substractResource : (ResourceType, Int) -> Resources -> Resources 
     substractResource (rType, quantity) res = 
      case rType of 
       Energy -> 
        { res | energy = energy - quantity } 
       -- TODO: same for Water and Metal 

     -- TODO: addResource function 

     hasEnoughResources newRes = 
      case negativeResources newRes of 
       -- TODO: function to check if any value of the new resources Record is negative 
       True -> -- return original resources if converter can not run 
        resources 
       False -> -- add outputs to res and return new recources 
        outputs |> List.foldr addResource newRes 
    in 
     intakes 
      |> List.foldr substractResource resources 
      |> hasEnoughResources 

奖励:梳子INE加减法,功能类型的功能1:(Int -> Int) -> (ResourceType, Int) -> Resources -> Resources并调用它像calcResource << (+)

updateGame : Model -> Model 
updateGame model = 
    let 
     newResources = model.converters |> List.foldr convert model.resources 
    in 
     { model | resources = newResources } 
+0

感谢您的想法!记录的这种用法确实有助于重构我的代码。我认为你的代码不会运行,因为你摆脱了资源类型,并且想要使用它们。此外updateGame函数与foldr的部分不适用于我。 –

+0

@GáborFekete我刚刚列出了我会改变的功能 - 只需保留ResourceType和Converter类型。我已经将convert函数的顺序更新为pipeable,并更正了substractResource的类型声明。 (我现在无法测试) – farmio

+0

我将当前工作的代码添加到了原始问题中。 –