F#删除尾部空间
我有这种方法,它需要一个列表并将它变成一个字节码字符串。它按我期望的方式工作;然而,我得到了一个我不想要的尾部空间。 问题:我该如何摆脱这最后的尾随0?F#删除尾部空间
Input: byteCode [SC 10; SC 2; SAdd; SC 32; SC 4; SC 5; SAdd; SMul; SAdd]
let rec byteCode (l : sInstr list) : string =
match l with
| [] -> ""
| (SC n :: l) -> "0 " + string n + " " + byteCode l
| (SAdd :: l) -> "1 " + byteCode l
| (SSub :: l) -> "2 " + byteCode l
| (SMul :: l) -> "3 " + byteCode l
| (SNeg :: l) -> "4 " + byteCode l
| (SLess :: l) -> "5 " + byteCode l
| (SIfze n :: l) -> "6 " + string n + " " + byteCode l
| (SJump n :: l) -> "7 " + string n + " " + byteCode l
这可能不会编译,因为我没有给我的整个程序。
This returns: "0 10 0 2 1 0 32 0 4 0 5 1 3 1 "
I expect: "0 10 0 2 1 0 32 0 4 0 5 1 3 1"
像这样的情况通常表明字符串是过于天真的方式连接的迹象。首先考虑收集你的结果的所有单个组件,然后调用预先String.concat
功能:
let byteCode (l : sInstr list) : string =
let rec byteCode' l =
match l with
| [] -> []
| (SC n :: l) -> "0" :: string n :: byteCode' l
| (SAdd :: l) -> "1" :: byteCode' l
| (SSub :: l) -> "2" :: byteCode' l
| (SMul :: l) -> "3" :: byteCode' l
| (SNeg :: l) -> "4" :: byteCode' l
| (SLess :: l) -> "5" :: byteCode' l
| (SIfze n :: l) -> "6" :: string n :: byteCode' l
| (SJump n :: l) -> "7" :: string n :: byteCode' l
l |> byteCode' |> String.concat " "
String.concat
已经只增加分隔字符串中的各个部分之间。
这也更清晰,因为它将特定分隔符字符串的实现细节保留在核心逻辑之外,并使其更容易替换 - 想象只需将其更改为函数中的两个空格即可。
或者,您可以使用您现有的功能,并在最终生成的字符串上调用.Trim()
(或.TrimEnd()
)方法来删除(尾随)空格。
外部byteCode函数不应该rec然后 –
除了你引用的优点,这也是更有效,因为'字符串。 concat'使用'StringBuilder'来创建结果字符串,这比链接更好。 – Tarmil
@HenrikHansen这是正确的;我忘了删除它。固定。 – TeaDrivenDev
你可以避开这种方式递归:
let byteCode (l : sInstr list) : string =
let instrToString (bc : sInstr) : string =
match bc with
| (SC n) -> sprintf "0 %d" n
| (SAdd ) -> "1"
| (SSub ) -> "2"
| (SMul ) -> "3"
| (SNeg ) -> "4"
| (SLess ) -> "5"
| (SIfze n) -> sprintf "6 %d" n
| (SJump n) -> sprintf "7 %d" n
l |> List.map instrToString |> String.concat " "
应该sInstr被定义为:
type sInstr =
| SC of int
| SAdd
| SSub
| SMul
| SNeg
| SLess
| SIfze of int
| SJump of int
职能,字节码和revserse看起来是这样的:
let byteCode (l : sInstr list) : string =
let instrToString (bc : sInstr) =
(match bc with
| SC n -> [0; n]
| SAdd -> [1]
| SSub -> [2]
| SMul -> [3]
| SNeg -> [4]
| SLess -> [5]
| SIfze n -> [6; n]
| SJump n -> [7; n])
String.Join(" ", (l |> List.map instrToString |> List.fold (fun acc lst -> acc @ lst) []))
let toInstr (bcString : string) : sInstr list =
let rec recToInstr bcList =
match bcList with
| [] -> []
| head :: tail ->
match head with
| "0" -> SC(Int32.Parse(tail.[0])) :: recToInstr (tail |> List.skip 1)
| "1" -> SAdd :: recToInstr tail
| "2" -> SSub :: recToInstr tail
| "3" -> SMul :: recToInstr tail
| "4" -> SNeg :: recToInstr tail
| "5" -> SLess :: recToInstr tail
| "6" -> SIfze(Int32.Parse(tail.[0])) :: recToInstr (tail |> List.skip 1)
| "7" -> SJump(Int32.Parse(tail.[0])) :: recToInstr (tail |> List.skip 1)
| _ -> []
recToInstr (bcString.Split(' ') |> Array.toList)
是的(尽管我仍然不会添加内联分隔符以避免重复)。递归方法对于实现相反的方向会更重要。 – TeaDrivenDev
@TeaDrivenDev:你是什么意思的重复? - 和相反的方向? –
复制:'sprintf'调用在多个位置包含用作分隔符的空格字符,因此更改(或只是确定)会涉及更多并容易出错。用“相反方向”,我的意思是把这里产生的“字节码”解析回原始表示。因为操作可能有或没有参数,所以不是每一步都会用相同数量的元素缩短列表的长度,所以不能只使用'map'。 – TeaDrivenDev
好,因为你甚至没有给出输入,我也看不出给定的代码甚至不能打印15,所以我们很难拼图把它重写出来 –
'sInstr'的类型声明会有帮助。请尽量始终提供可编译的代码示例,因此希望帮助您的人不会不必要地寻找提示。 – TeaDrivenDev
从根本上说,你可以用“1”+“something”和“something =”“'所以你可以用'1''。最简单的解决方案可能只是运行trimend –