【问题标题】:creating stack in sml在 sml 中创建堆栈
【发布时间】:2016-03-14 20:22:21
【问题描述】:

我正在尝试在 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

【问题讨论】:

    标签: sml smlnj


    【解决方案1】:

    我建议在列表的前面进行推送和弹出,通过模式匹配实现实际的推送和弹出,并将(修改后的)堆栈作为参数传递。

    假设你有一个字符串列表,例如:

    ["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
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 正如其他地方评论的那样,如果您将新元素添加到前面而不是后面,列表是理想的堆栈。 it = number :: it.
      【解决方案3】:

      我会将问题拆分为不同的关注点;特别是,将执行 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-02
        • 1970-01-01
        • 1970-01-01
        • 2013-04-03
        • 2015-06-16
        • 1970-01-01
        • 2017-10-18
        • 2017-06-06
        相关资源
        最近更新 更多