2016-03-04 88 views
2

我想在sml中创建堆栈,我尝试过使用列表;但我无法将元素添加到列表中。我试图从输入文件读取行,说如果行说:在sml中创建堆栈

push 5 
push 9 
add 
quit 

然后我想输出文件是:

14 

因为5 + 9是14 到目前为止,我能够创建布尔函数来识别行是推还是数字。

fun is_digit (c) = #"0" <= c andalso c <= #"9"; 
fun is_Push (c) = String.isSubstring "push" c; 

fun stack(inFile : string, outFile : string) = 
let 
     val ins = TextIO.openIn inFile; 
     val outs = TextIO.openOut outFile; 
     val readLine = TextIO.inputLine ins; 
     val it = []: string list; 
     fun helper(readLine : string option) = 
      case readLine of 
       NONE => (TextIO.closeIn ins; TextIO.closeOut outs) 
       | SOME(c) => (
        if is_Push c 
        then 
        let 
         val number = String.sub(c,5); 
         val numbChar = Char.toString number; 

        in 
         val myList = nil :: numbChar; 
         TextIO.output(outs, Int.toString(length myList)) 
        end 

        else 
         TextIO.output(outs, "aaa\n"); 
         helper(TextIO.inputLine ins)) 

in 
     helper(readLine) 
end 

回答

5

我会推荐push和popping发生在列表的前面,通过模式匹配实现的实际推送和弹出,以及作为参数传递的(修改)堆栈。

假设你有一个看起来像例如

["push 5", "push 9", "add", "quit"] 

并且要根据以下规则来处理这个字符串:

1) If the string is of the form "push d" (where d is a single digit) then 
    push the integer value of d onto the stack 

2) If the string is of the form "add" then pop the first two elements off 
    the stack, add them, and push the sum back on 

3) If the string is "quit" then return the top of the stack 

万一3,实际上返回一个值,在其他情况下 - 呼叫处理功能上尾行列表和适当修改的堆栈。喜欢的东西:

fun process ([], stack) = 0 
| process ("quit"::lines, i::stack) = i 
| process ("add"::lines, i::j::stack) = process(lines,(i+j)::stack) 
| process (s::lines, stack) = 
     let 
      val d = String.sub(s,5) 
      val i = Char.ord d - Char.ord(#"0") 
     in 
      process(lines,i::stack) 
     end; 

我行对空列表返回0的基础情况下扔,但没有提供真正的错误检查。特别是 - 如果在堆栈少于2个元素时遇到“add”,则会出现运行时错误,如果使用空堆栈调用“quit”,则会导致崩溃。

要使用此,用线列表和一个空栈称之为:

- process (["push 5", "push 9", "add", "quit"],[]); 
val it = 14 : int 
1

使用@添加到列表中。例如。 it = it @ [number];

顺便说一句,我建议你重命名csline因为c通常用于单个字符,而不是字符的字符串。这让您的程序的读者感到困惑。

+0

正如其他地方所评论的,如果您将新元素添加到前面而不是后面,则列表是理想的堆栈。 'it = number :: it'。 –

0

我会分裂问题为单独的担忧;特别是将执行I/O的函数与纯函数分开。这使得它们更容易测试,并且稍后更容易编写。

  1. 定义堆栈命令的抽象表示并将您的文件转换为此抽象表示形式。定义处理不正确的堆栈命令的异常类型。这意味着您的数据表示已被清理。

    datatype StackCommand = Push of int 
             | Pop 
             | Add 
             | Quit 
    
    exception InvalidCommand of string * string list 
    
  2. 创建用于从文件中读取行的可重复使用的辅助函数。

    fun isLinebreak c = c = #"\n" 
    
    fun inputLines filename = 
        let val fd = TextIO.openIn filename 
         val content = TextIO.inputAll fd 
         val _ = TextIO.closeIn fd 
        in String.tokens isLinebreak content 
        end 
    
  3. 独立解析一个命令到一个功能的关注,并使其以便它需要的参数为正确的命令正确的号码。

    fun parseCommand line = 
        case String.tokens Char.isSpace line of 
         ["push", s] => (case Int.fromString s of 
              SOME i => Push i 
              | NONE => raise InvalidCommand (push, s)) 
         | ["pop"] => Pop 
         | ["add"] => Add 
         | ["quit"] => Quit 
         | (cmd::args) => raise InvalidCommand (cmd, args) 
    
    val parseCommands = map parseCommand 
    
    val inputCommands = parseCommands o inputLines 
    
  4. 将堆栈定义为整数列表。给定一个堆栈,通过迭代这个列表并根据命令更新堆栈来评估命令列表。

    type stack = int list 
    exception StackError of stack * StackCommand 
    
    fun evalCommand stack (Push i) = i::stack 
        | evalCommand (_::stack) Pop = stack 
        | evalCommand (i::j::stack) Add = i+j::stack 
        | evalCommand stack Quit = stack 
        | evalCommand stack cmd = raise StackError (stack, cmd) 
    
    fun evalCommands stack [] = stack 
        | evalCommands stack (Quit::_) = stack 
        | evalCommands stack (cmd::cmds) = 
        evalCommands (evalCommand stack cmd) cmds 
    

然后可以对inputCommands返回值请使用evalCommands,或者你可以在一个REPL,从标准输入交互读取使用evalCommand