州Monad的麻烦

时间:2014-10-16 03:15:53

标签: haskell ghc

我正在尝试编写一个程序来生成'单词链,例如蝙蝠 - >猫 - >婴儿床 - >机器人,使用列表monad(主要是理解)来生成单词的组合,并使用状态monad来构建实际的链,因为我经历了各种可能性。第二部分给了我麻烦:

import Control.Monad.State

type Word = String
type Chain = [Word]

getNext :: Word -> State Chain Word
getNext word = do
  list <- get
  return (list ++ "current word")

我生成单词的部分起作用并在下面给出,但是你可以看到我真的不知道我在这部分做了什么。基本上wordVariations :: Word -> [Word]接受一个Word并返回一个字母列表,这些字母在给定单词的一个字母中有所不同。我试图改变这一点,以便每个单词都有一个表示其前辈的状态:

例如:input =&#34; cat&#34;。最终的价值是&#34;得到&#34;,最终的状态是[&#34; cat&#34;,&#34; cot&#34;,&#34;得到&#34;]

我现在拥有的东西会给我&#34;得到&#34;来自&#34; cat&#34;经过3个步骤,但不会告诉我它是如何到达那里的。

我在网上找到的State Monad教程都不是非常有帮助的。上面的代码在使用GHC编译时会出现错误:

WordChain.hs:42:11:
    Couldn't match type `Word' with `Char'
    When using functional dependencies to combine
      MonadState s (StateT s m),
        arising from the dependency `m -> s'
        in the instance declaration in `Control.Monad.State.Class'
      MonadState [Char] (StateT Chain Data.Functor.Identity.Identity),
        arising from a use of `get' at WordChain.hs:42:11-13
    In a stmt of a 'do' block: list <- get
    In the expression:
      do { list <- get;
           return (list ++ "current word") }
Failed, modules loaded: none.

这只是为了解决问题,但我无法解决这个问题!

完整的代码如下有用。我知道这可能不是最聪明的方法,但这是一个了解州monad的好机会。我对代码工作方式的必要改变持开放态度,因为我怀疑会调用一些主要的重构:

import           Control.Monad.State

type Word  = String
type Dict  = [String]
-- data Chain = Chain [Word] deriving (Show)

replaceAtIndex :: Int -> a -> [a] -> [a]
replaceAtIndex n item ls = a ++ (item:b) where (a, (_:b)) = splitAt n ls

tryLetter str c = [replaceAtIndex n c str | n <- [0..(length str - 1)]]

wordVariations str = tryLetter str  =<< ['a' .. 'z']

testDict :: Dict
testDict = ["cat","cog","cot","dog"]

------- implement state to record chain

type Chain = [Word] -- [cat,cot,got,tot], etc. state var.

startingState = [""] :: Chain

getNext :: Word -> State Chain Word
getNext w = do
  list <- get
  return ( list ++ "current word")

1 个答案:

答案 0 :(得分:2)

首先,您发布的错误位于此行return ( list ++ "current word")

  • "current word"类型为Word,这是String, which is an alias for [Char]的别名。

  • 变量list的类型为Chain,它是[Word]的别名,是[[Char]]的别名。

    < / LI>
  • 函数的类型签名强制返回类型必须是Word。

  • ++要求双方的类型都是具有相同类型的列表(++) :: [a] -> [a] -> [a]

  • 但是,如果您插入上述类型签名,则会获得与“a”不匹配的[Word] -> [Char] -> [Char]类型。


快速但相当重要的性能方面注意事项:在列表前添加比添加更快,因此您可能需要考虑向后构建它们并使用(:)并在最后反转它们。


State Monad并不是存储用于获得结果的步骤的正确选择。当List Monad足以完成任务时,至少它肯定是矫枉过正的。考虑:

-- given a list of words, find all possible subsequent lists of words
getNext :: [String] -> [[String]]
getNext words@(newest:_) = fmap (:words) (wordVariations newest)

-- lazily construct all chains of every length for every word
wordChains :: String -> [[[String]]]
wordChains word = chain
  where chain = [[word]] : map (>>= getNext) chain

-- all the 5 word long chains starting with the word "bat"
batchains = wordChains "bat" !! 4

(免责声明:代码已编译,但未运行)。

getNext接受一个Chain,并返回一个包含链表的列表,其中每个链在链中都有不同的前置后继。由于它们共享一个共同的尾部,因此它具有内存效率。

在wordChains中,通过使用列表monad与(&gt;&gt; = getNext)重复映射,最终会得到一个懒惰的无限列表,其中第0个项目是一个Chain起始单词,第一个项目是全部2个项目第一个是起始单词的链,第二个项是3个项目链,依此类推。如果你只想要一条长度为5的链条,你可以抓住第4项的头部,它会做足够的计算来为你创造,但你也可以得到所有这些。

此外,getNext可以扩展为不通过过滤重复单词。


最后,当涉及到找到wordVariations时,另一种算法是构造一个过滤器,如果两个单词的长度相同并且它们之间不同的字符数正好为1则返回True。然后你可以过滤在字典上,而不是尝试每一个可能的字母变化。

祝你好运!