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" 
+1

好,因为你甚至没有给出输入,我也看不出给定的代码甚至不能打印15,所以我们很难拼图把它重写出来 –

+0

'sInstr'的​​类型声明会有帮助。请尽量始终提供可编译的代码示例,因此希望帮助您的人不会不必要地寻找提示。 – TeaDrivenDev

+1

从根本上说,你可以用“1”+“something”和“something =”“'所以你可以用'1''。最简单的解决方案可能只是运行trimend –

像这样的情况通常表明字符串是过于天真的方式连接的迹象。首先考虑收集你的结果的所有单个组件,然后调用预先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())方法来删除(尾随)空格。

+0

外部byteCode函数不应该rec然后 –

+0

除了你引用的优点,这也是更有效,因为'字符串。 concat'使用'StringBuilder'来创建结果字符串,这比链接更好。 – Tarmil

+1

@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) 
+0

是的(尽管我仍然不会添加内联分隔符以避免重复)。递归方法对于实现相反的方向会更重要。 – TeaDrivenDev

+0

@TeaDrivenDev:你是什么意思的重复? - 和相反的方向? –

+0

复制:'sprintf'调用在多个位置包含用作分隔符的空格字符,因此更改(或只是确定)会涉及更多并容易出错。用“相反方向”,我的意思是把这里产生的“字节码”解析回原始表示。因为操作可能有或没有参数,所以不是每一步都会用相同数量的元素缩短列表的长度,所以不能只使用'map'。 – TeaDrivenDev