【发布时间】:2015-03-12 03:40:58
【问题描述】:
我遇到了一些问题,但找不到原因。我目前正在使用最新版本的 GHCi 便携版 - 但要面对事实:这是我第一次使用 Haskell,所以像往常一样,问题可能是用户而不是系统...
出现的问题包括:
- 我不完全确定我是否理解了
let x = 0和x <- 0之间的区别。我知道let是与纯函数一起使用的,<-与副作用(IO)一起使用?请有人再次向我解释一下。 - 我的类型不匹配,即
String和(String,[Char])(有时还有其他......)。编译器告诉我String是预期的,尽管我明确地将函数定义为(String,String)。这是怎么回事?我是不是在某个地方对模式匹配有误? - 递归没有按预期工作(即,显然根本没有工作)。如果有人可以帮助我,我将不胜感激。
这是我想做的:
我正在尝试编写一个小程序来实现一个接受单词的有限状态机。这意味着它需要一组状态,其中一个是开始状态、一个接受状态列表和一些转换规则。 (表示可能的输入和状态的字母表有些隐含。)我不想在这里详细介绍 FSM。
但是,这就是我想出一种定义此类 FSM 的方法的方式:
"a(b+|c+)"
"start"
["b","c"]
[
("start", [('a',"a"), ('_',"reject")]),
("a", [ ('b',"b"), ('c',"c"), ('_',"reject")]),
("b", [ ('b',"b"), ('_',"reject")]),
("c", [ ('c',"c"), ('_',"reject")]),
("reject", [ ('_',"reject")])
]
在第一行,我们简要描述了 FSM 应该接受的内容(在本例中以正则表达式的形式)。它仅用于显示一次。
第二行定义开始状态,第三行是接受状态列表。
以下所有行一起是转换规则。在这个例子中,如果我们处于“开始”状态并读取输入“a”,则下一个状态是“a”,如果我们读取其他任何内容,则为“拒绝”。 (我知道我还没有实现 '_' 表示 else,如果读取的输入没有定义转换,程序将崩溃。)
所以程序来了:
module FSM where
import System.IO
main :: IO ()
main = do
putStr "Enter file name: "
fileName <- getLine
(description, startState, acceptingStates, stateTransitions) <- (readDef fileName)
putStrLn ("FSM description: " ++ description)
putStr "Enter FSM input: "
input <- getLine
let input = reverse input
putStrLn "----------------"
let (finalState, oldStates) = changeState input startState stateTransitions
putStrLn (oldStates ++ finalState)
checkAcception finalState acceptingStates
--reads the specified .fsm file and returns
-- the description of the FSM (first line),
-- the start state (second line),
-- the list of accepting states (third line),
-- and the list of tuples containing all states and transitions (remaining lines)
readDef :: String -> IO (String, String, [String], [(String, [(Char,String)])])
readDef fileName = do
contents <- readFile (fileName ++ ".fsm")
let lineList = lines contents
let description = read (head lineList)
let startState = read (lineList !! 1)
let acceptingStates = read (lineList !! 2)
let stateTransitions = read (filter (/='\t') (concat (drop 3 lineList)))
return (description, startState, acceptingStates, stateTransitions)
--recursive function that takes the input, start state, and state transitions
--and computes the new state by a call to itself with the old state and a part of the input
changeState :: String -> String -> [(String, [(Char,String)])] -> (String, String)
changeState startState [] _ = (startState, "")
changeState startState (x:xs) stateTransitions = do
let (currentState, oldStates) = changeState xs startState stateTransitions
let newState = findKey x (findKey currentState stateTransitions)
let oldStates = (oldStates ++ currentState ++ " -(" ++ [x] ++ ")-> ")
return (newState, oldStates)
--helper function to find a key in a list of tuples and return the corresponding value
--(because we are not using the map notation in the .fsm file)
findKey :: (Eq k) => k -> [(k,v)] -> v
findKey key xs = snd . head . filter (\(k,v) -> key == k) $ xs
--checks for a given state whether or not it is in the list of accepting states
checkAcception :: String -> [String] -> IO ()
checkAcception finalState acceptingStates = do
let accept = any (==finalState) acceptingStates
if accept
then putStrLn "Input accepted!!"
else putStrLn "Input rejected!!"
这个想法是让用户选择一个从中加载定义的文件(readDef,就像一个魅力)。然后提示他输入 FSM 处理的一些输入。
递归的 changeState 然后执行实际工作(也不能正常工作......)。
最后,显示状态和转换的顺序,并检查最终状态是否为接受状态(checkAcceptance)。
现在,不要试图优化我写的内容。我知道,定义建模的方式可以改进,我写的许多行可以使用一些高阶 Haskell foo 写得更短。但请帮我解决上面列出的问题(当然也帮我解决问题)。
提前非常感谢。
最后一件事:我正在为我大学的一个研讨会尝试一些 Haskell,所以如果软件架构小组的某个人在谷歌上搜索了我的代码并读到:嗨 :)
【问题讨论】:
-
请从头开始。编写一个函数。确保它编译。测试一下。如果您遇到问题,请在此处询问有关 one 功能的问题。然后继续下一个。在这里,你丢掉了一堆乱七八糟的代码并说“它不起作用。帮帮我”。没有太多人会对挖掘所有这些感兴趣。
-
不相关,看起来您正在使用字符串来表示 LISP 或 Scheme 程序员将使用符号的目的。在 Haskell 中,我们通常会尽可能地尝试使用枚举类型。写
data FSMState = Start | Stop | A | ...之类的东西。然而,在 Haskell 中通常有更自然的 FSM 表示。 -
@dfeuer 好像我没有想到...我很清楚我发布的内容不是一个简洁的问题。但我想我表示我不确定问题出自代码的哪部分,所以我最好发布全部。最重要的是,我经常阅读那些过短的问题会导致更多问题的帖子,因为上下文甚至含义都不清楚。因此,为了完善我的要求:我特别不确定
changeState部分。问题仍然存在:我对let的理解正确吗?我是否造成了明显的类型不匹配?递归的想法正确吗? -
不,
changeState在我看来毫无意义。据我所知,你根本不需要/想要一个do块,而且我不知道你对“旧状态”的意思。不是只有一个当前状态和一个下一个状态吗? -
是的,当然总是只有一个当前状态和一个下一个状态。但是,如果我想将正在使用的状态打印到命令行,即从开始状态到下一个到下一个状态的转换......到最终状态,我看到两种方法:1.在递归函数中执行此操作。然后我需要将其定义为 IO,但实际上希望保持其纯净。 2. 递归地建立一个字符串 oldStates,我将它与函数一起返回,然后可以打印出来。
标签: haskell recursion io pattern-matching type-mismatch