문제

How to modify below code to Return "string" so that returned output displayed on my MVC page and also would like to accept enteredChar from user.

Is there better way to do create this pyramid?

Current code:

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)

Output:

     A
    ABA
   ABCBA
  ABCDCBA
 ABCDEDCBA
ABCDEFEDCBA
도움이 되었습니까?

해결책

A few small optimizations could be:

  • Use StringBuilder instead of printf which is quite slow with long strings.
  • Use Array instead of List since Array works better with String.

Here is a version producing a pyramid string, which is kept closely to your code:

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"

다른 팁

As an alternative to Daniel's solution, you can achieve what you want with minimal changes to the code logic. Instead of using printf that writes the output to the console, you can use Printf.bprintf which writes the output to a specified StringBuilder. Then you can simply get the resulting string from the StringBuilder.

The modified function will look like this. I added parameter str and replaced printf with Printf.bprintf str (and printfn with bprintf together with additional \n char):

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

To call the function, you first create StringBuilder and then pass it to myfunc in every call. At the end, you can get the result using ToString method:

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

I think Daniel's solution looks nicer, but this is the most direct way to tunr your printing code into a string-building code (and it can be done, pretty much, using Search & Replace).

If I understand your question (this likely belongs on Code Review) here's one way to rewrite your function:

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))

Example

showPyramid Console.Out 'F'

or, to output to a string

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

EDIT

After seeing Tomas' answer I realized I skipped over "return a string" in your question. I updated the code and added examples to show how you could do that.

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"

Here's another approach that seems to be a good demonstration of functional composition. I bet it's the shortest solution among the answers here. :)

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")
  • First, we generate an unusual array of initial data. Its element [0] contains a space or whatever separator you want to have; I preferred dash (-) for debugging purposes.
  • The (1) line makes a function that calculates what character to be placed. The result of row-abs(col-len+1)+1 can be either positive (and there is a char to be placed) or zeronegative, and there should be a space. Note that there is no if statement: it is hidden within the max function;
  • The (2) line composes a function int -> string for generating an individual row;
  • The (3) line passes the function above as argument for sequence initializer.

The three lines can be written in a more verbose way:

    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

Note genRow needs no formal argument due to functional composition: the argument is being bound into genCell, returning a function of a single argument, exactly what Seq.init needs.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top