在Haskell中实现蛋糕烘焙状态机

2eafrhcq  于 2023-06-23  发布在  其他
关注(0)|答案(2)|浏览(166)

再一次,我不能把我的头绕在这块蛋糕上:

data Event = AddEggs | AddFlour | AddSugar | Mix | Bake
  deriving (Eq, Show)

目前的蛋糕烘焙状态为:

data State = Start | Error | Finished | HasEggs | HasFlour | HasSugar | HasFlourAndSugar | Mixed 
  deriving (Eq, Show)

状态机从状态Start开始,在ErrorFinished停止。
两个有效的状态序列是:

  1. [AddEggs, AddFlour, AddSugar, Mix, Bake]
  2. [AddEggs, AddSugar, AddFlour, Mix, Bake]
    请注意,AddFlourAddSugar可以以两种不同的顺序出现。此外,在Bake之后,您可以添加更多事件,并且状态机仍保留在FinishedError中。最后,如果状态顺序被破坏,它将进入Error状态,没有事件可以“保存”它。

我最大的努力

data Event = AddEggs | AddFlour | AddSugar | Mix | Bake
  deriving (Eq, Show)

data State = Start | Error | Finished | HasEggs | HasFlour | HasSugar | HasFlourAndSugar | Mixed 
  deriving (Eq, Show)

stepHelper :: State -> Event -> Bool -> Bool -> State

stepHelper Start AddEggs False False = HasEggs
stepHelper Start AddEggs True _ = Error
stepHelper Start AddEggs _ True = Error
stepHelper Start _ _ _ = Error

stepHelper HasEggs AddFlour has_flour has_sugar = if has_flour then Error else HasFlour
stepHelper HasEggs AddSugar has_flour has_sugar = if has_sugar then Error else HasSugar

stepHelper HasEggs AddFlour _ has_sugar = stepHelper HasEggs AddFlour True has_sugar
stepHelper HasEggs AddSugar has_flour _ = stepHelper HasEggs AddSugar has_flour True
stepHelper _ Mix has_flour has_sugar = if has_flour && has_sugar then Mixed else Error
stepHelper Mixed Bake _ _ = Finished

step :: State -> Event -> State
step s e = stepHelper s e False False

-- do not edit this
bake :: [Event] -> State
bake events = go Start events
  where go state [] = state
        go state (e:es) = go (step state e) es

运行MWE

1.克隆this repo
1.在my-haskell-mooccdexercises
1.运行stack runhaskell Set7Test.hs

错误信息

经过以上三个步骤,我得到:

Set7.hs:61:1: warning: [-Wtabs]
    Tab character found here, and in 29 further locations.
    Please use spaces instead.
   |
61 |         ------------------------------------------------------------------------------
   | ^^^^^^^^

Set7.hs:119:29: error: parse error on input `='
    |
119 |             go state (e:es) = go (step state e) es
wbgh16ku

wbgh16ku1#

编译器警告是稍后出现错误的预兆。你把空格和制表符混在一起了,你做错了。缩进和对齐在Haskell中很重要。查看你的Github repo,看看这个函数是如何呈现的:

-- do not edit this
bake :: [Event] -> State
bake events = go Start events
  where go state [] = state
            go state (e:es) = go (step state e) es

如果你不编辑,我保证它永远不会编译。它需要对齐。
看起来像在2c9877caf中向一堆行添加了制表符。它们中的大多数都是无害的,因为它们在注解中,但是如果您从不在源文件中插入制表符,那么您的生活会更轻松。

pjngdqdw

pjngdqdw2#

你的状态转换函数应该只带两个参数:当前状态和行动。一个单一的状态应该包含你到目前为止所做的所有信息。你有五个可以影响状态的操作,这意味着你至少有32个不同的状态。由于某些操作序列显然是无效的(您不能在添加所有成分之前进行烘焙),因此可以将其中许多状态折叠为Error状态。例如,不需要表示烘焙但未混合的配料集的状态。但是像HasEggsAndFlourHasEggs(暗示缺少面粉)是两种不同的状态。

data Event = AddEggs | AddFlour | AddSugar | Mix | Bake
  deriving (Eq, Show)

与其立即枚举状态,不如开始定义step函数,看看需要定义哪些状态。比如说

step :: State -> Event -> State
step Error _ = Error -- Can't recover from an error
step Finished _ = Error  -- Not clear what you can do with a finished cake
-- You can start a cake by adding any one ingredient
step Start AddEggs = HasEggs
step Start AddFlour = HasFlour
step Start AddSugar = HasSugar
step Start _ = Error -- Cannot yet mix or bake
-- Once there are eggs, there are only two more ingredients you can add
step HasEggs AddFlour = HasEggsAndFlour
step HasEggs AddSugar = HasEggsAndSugar
step HasEggs _ = Error  -- Can't mix, bake, or add more eggs
...
step HasEggsSugarFlour Mix = ReadyToBake
step HasEggsSugarFlour _ = Error  -- can't add anything else, can't bake yet
step ReadyToBake Bake = Finished -- can't do anything except bake
step ReadyToBake _ = Error
step Error _ = Error

定义状态的方式可能会有所不同。上面,我暗示了每个状态的单独的空值构造器。您可以将其分解为可以组合的更简单的类似布尔的状态(以避免布尔盲)。比如说

data Egged = HasEggs | HasNoEggs
data Floured = HasFlour | HasNoFlour
data Sugared = HasSugar | HasNoSugar
data Mixed = Mixed | Unmixed
data Baked = Baked | Unbaked

data State = Start | Finished | Error | InProgress Egged Floured Sugared Mixed

step :: State -> Event -> State
step Error _ = Error
step Finished _ = Error
-- When can and can't we add eggs:
step Start AddEggs = InProgress HasEggs HasNoFlour HasNoSugar Unmixed
step (InProgress HasNoEggs flour sugar Unmixed Unbaked) AddEggs = InProgress HasEggs flour sugar Unmixed
step (InProgress HasEggs _ _ _ _) = Error
-- When can and can't we add sugar
...
-- When can we bake
step (InProgress HasEggs HasFlour HasSugar Mixed Unbaked) Bake = Finished
step _ Bake = Error

相关问题