在 sml 中创建堆栈
creating stack in 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
使用@
添加到列表。例如。 it = it @ [number];
顺便说一下,我建议您将 c
重命名为 s
或 line
,因为 c
通常用于单个字符,而不是字符串。这会让你的程序的人 reader 感到困惑。
我建议将入栈和出栈放在列表的前面,实际入栈和出栈是通过模式匹配实现的,并将(修改后的)堆栈作为参数传递。
假设您有一个字符串列表,例如
["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 中,您实际上 return 一个值,在其他情况下 - 在行列表的尾部调用处理函数并使用适当修改的堆栈。类似于:
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;
我在一个空的行列表中加入了 returning 0
的基本情况,但没有提供真正的错误检查。特别是 - 如果在堆栈少于 2 个元素时遇到 "add",它将因 运行 时间错误而崩溃,如果使用空堆栈调用 "quit" 将导致崩溃。
要使用它,请使用行列表和空堆栈调用它:
- process (["push 5", "push 9", "add", "quit"],[]);
val it = 14 : int
我会把问题分解成不同的关注点;特别是,将执行 I/O 的函数与纯函数分开。这使它们更易于测试,以后更容易编写。
定义堆栈命令的抽象表示并将您的文件转换为该抽象表示。定义一个异常类型来处理不正确的堆栈命令。这意味着您的数据表示已清理。
datatype StackCommand = Push of int
| Pop
| Add
| Quit
exception InvalidCommand of string * string list
创建可重用的辅助函数以从文件中读取行。
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
将解析单个命令的关注点分离到一个函数中,并使其需要正确命令的正确数量的参数。
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
将堆栈定义为整数列表。给定一个堆栈,通过迭代此列表并根据命令更新堆栈来评估命令列表。
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
的 return 值上使用 evalCommands
,或者您可以在从标准输入交互读取的 REPL 中使用 evalCommand
。
我正在尝试在 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
使用@
添加到列表。例如。 it = it @ [number];
顺便说一下,我建议您将 c
重命名为 s
或 line
,因为 c
通常用于单个字符,而不是字符串。这会让你的程序的人 reader 感到困惑。
我建议将入栈和出栈放在列表的前面,实际入栈和出栈是通过模式匹配实现的,并将(修改后的)堆栈作为参数传递。
假设您有一个字符串列表,例如
["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 中,您实际上 return 一个值,在其他情况下 - 在行列表的尾部调用处理函数并使用适当修改的堆栈。类似于:
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;
我在一个空的行列表中加入了 returning 0
的基本情况,但没有提供真正的错误检查。特别是 - 如果在堆栈少于 2 个元素时遇到 "add",它将因 运行 时间错误而崩溃,如果使用空堆栈调用 "quit" 将导致崩溃。
要使用它,请使用行列表和空堆栈调用它:
- process (["push 5", "push 9", "add", "quit"],[]);
val it = 14 : int
我会把问题分解成不同的关注点;特别是,将执行 I/O 的函数与纯函数分开。这使它们更易于测试,以后更容易编写。
定义堆栈命令的抽象表示并将您的文件转换为该抽象表示。定义一个异常类型来处理不正确的堆栈命令。这意味着您的数据表示已清理。
datatype StackCommand = Push of int | Pop | Add | Quit exception InvalidCommand of string * string list
创建可重用的辅助函数以从文件中读取行。
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
将解析单个命令的关注点分离到一个函数中,并使其需要正确命令的正确数量的参数。
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
将堆栈定义为整数列表。给定一个堆栈,通过迭代此列表并根据命令更新堆栈来评估命令列表。
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
的 return 值上使用 evalCommands
,或者您可以在从标准输入交互读取的 REPL 中使用 evalCommand
。