返回F#中的字符串和代码优化

时间:2012-07-06 14:25:20

标签: performance f# f#-interactive

如何修改下面的代码以返回“string”,以便返回的输出显示在我的MVC页面上,并且也希望接受来自用户的enteredChar

有没有更好的方法来创建这个金字塔?

当前代码:

let enteredChar = 'F' // As Interactive window doesn't support to Read Input
let mylist = ['A'..enteredChar] 
let mylistlength = mylist |> List.length
let myfunc i x tlist1 =
    (for j = 0 to mylistlength-i-2 do printf "%c" ' ')
    let a1 = [for p in tlist1  do if p < x then yield p]
    for p in a1 do printf "%c" p
    printf "%c" x
    let a2 = List.rev a1
    for p in a2 do printf "%c" p
    printfn "%s" " "

mylist |> List.iteri(fun i x -> myfunc i x mylist)

输出:

     A
    ABA
   ABCBA
  ABCDCBA
 ABCDEDCBA
ABCDEFEDCBA

5 个答案:

答案 0 :(得分:4)

一些小的优化可能是:

  • 使用StringBuilder代替printf,这对于长字符串而言非常慢。
  • 使用Array代替List,因为Array可以更好地与String合作。

这是一个产生金字塔字符串的版本,它与您的代码紧密相关:

open System
open System.Text

let generateString c = 
    let sb = StringBuilder()             
    let generate i x arr =
        String.replicate (Array.length arr-i-1) " " |> sb.Append |> ignore
        let a1 = Array.filter (fun p -> p < x) arr
        String(a1) |> sb.Append |> ignore
        sb.Append x |> ignore
        String(Array.rev a1) |> sb.Append |> ignore
        sb.AppendLine " " |> ignore

    let arr = [|'A'..c|]               
    arr |> Array.iteri(fun i x -> generate i x arr)
    sb.ToString()

generateString 'F' |> printfn "%s"

答案 1 :(得分:2)

如果我理解你的问题(这可能属于Code Review),这是重写你的功能的一种方法:

let showPyramid (output: TextWriter) lastChar =
  let chars = [|'A' .. lastChar|]

  let getRowChars n = 
    let rec loop acc i =
      [|
        if i < n then let c = chars.[i] in yield c; yield! loop (c::acc) (i+1)
        else yield! List.tail acc
      |]
    loop [] 0

  let n = chars.Length
  for r = 1 to n do 
    output.WriteLine("{0}{1}{0}", String(' ', n - r), String(getRowChars r))

实施例

showPyramid Console.Out 'F'

或,输出到字符串

use output = new StringWriter()
showPyramid output 'F'
let pyramid = output.ToString()

修改

看到Tomas的回答后,我意识到我在你的问题中跳过了“返回一个字符串”。我更新了代码并添加了一些示例来说明如何做到这一点。

答案 2 :(得分:2)

作为Daniel解决方案的替代方案,您可以通过对代码逻辑的最小更改来实现您的目标。您可以使用printf将输出写入指定的Printf.bprintf,而不是使用StringBuilder将输出写入控制台。然后,您只需从StringBuilder获取结果字符串。

修改后的功能如下所示。我添加了参数str,并将printf替换为Printf.bprintf strprintfn替换为bprintf以及其他\n字符:

let myfunc i x tlist1 str = 
    (for j = 0 to mylistlength-i-2 do Printf.bprintf str "%c" ' ') 
    let a1 = [for p in tlist1  do if p < x then yield p] 
    for p in a1 do Printf.bprintf str "%c" p 
    Printf.bprintf str "%c" x 
    let a2 = List.rev a1 
    for p in a2 do Printf.bprintf str "%c" p 
    Printf.bprintf str "%s\n" " " 

要调用此功能,首先要创建StringBuilder,然后在每次通话中将其传递给myfunc。最后,您可以使用ToString方法获得结果:

let str = StringBuilder()
mylist |> List.iteri(fun i x -> myfunc i x mylist str) 
str.ToString()

我认为Daniel的解决方案看起来更好,但这是将您的打印代码调整为字符串构建代码的最直接方式(并且可以使用Search&amp; Replace来完成它。)

答案 3 :(得分:2)

let pyramid (ch:char) = 
  let ar = [| 'A'..ch |]
  let len = ar.Length
  Array.mapi
    (fun i ch -> 
      let ar = ar.[0..i]
      String.replicate (len - i - 1) " " + new string(ar) + new string((Array.rev ar).[1..]))
    ar
  |> String.concat "\n" 

pyramid 'F' |> printfn "%s"

答案 4 :(得分:2)

这是另一种似乎是功能组合的良好示范的方法。我敢打赌,这是答案中最短的解决方案。 :)

let charsToString = Seq.map string >> String.concat String.Empty
let pyramid lastChar =
    let src = '-'::['A'..lastChar] |> List.toArray
    let len = Array.length src - 1

    fun row col -> row-abs(col-len+1)+1 |> max 0 |> Array.get src   // (1)
    >> Seq.init (len*2-1) >> charsToString                          // (2)
    |> Seq.init len                                                 // (3)

pyramid 'X' |> Seq.iter (printfn "%s")
  • 首先,我们生成一组不寻常的初始数据。它的元素[0]包含一个空格或你想拥有的任何分隔符;我首选dash(-)用于调试目的。
  • (1)行生成一个函数,用于计算要放置的字符。 row-abs(col-len+1)+1的结果可以是正面(并且要放置一个字符)或 zeronegative ,并且应该有一个空格。请注意,没有if语句:它隐藏在max函数中;
  • (2)行组成一个函数int -> string,用于生成单个行;
  • (3)行将上面的函数作为序列初始值设定项的参数传递。

这三行可以用更详细的方式编写:

    let genCell row col = row-abs(col-len+1)+1 |> max 0 |> Array.get src 
    let genRow = genCell >> Seq.init (len*2-1) >> charsToString
    Seq.init len genRow

注意由于功能组合,genRow不需要正式参数:参数被绑定到genCell,返回单个参数的函数,正是Seq.init所需要的。