我正在尝试编写一个程序来生成'单词链,例如蝙蝠 - >猫 - >婴儿床 - >机器人,使用列表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")
答案 0 :(得分:2)
首先,您发布的错误位于此行return ( list ++ "current word")
。
"current word"
类型为Word
,这是String, which is an alias for
[Char]的别名。
变量list
的类型为Chain
,它是[Word]
的别名,是[[Char]]
的别名。
函数的类型签名强制返回类型必须是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。然后你可以过滤在字典上,而不是尝试每一个可能的字母变化。
祝你好运!