HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Print diamond of any size

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
diamondanyprintsize

Problem

I'm trying to print a diamond of asterisks of an arbitrary size. I'm new to F#, so I'm interested in virtually any feedback, but especially adherence to idioms, convention, and functional orientation.

module Diamond
open System

let rangeWithInverse (max) = 
    List.zip [ 1 .. max ] [ max .. -1 .. 1]

let listAscDesc (list) = 
    list @ (list |> List.rev |> List.skip 1)

let starsAndSpacesString (stars: int, spaces: int): string =
    String.replicate spaces " " + String.replicate ((stars * 2) - 1) "*"

let diamond =
    rangeWithInverse 18
        |> listAscDesc
        |> Seq.map starsAndSpacesString
        |> Seq.reduce (fun a b -> a + "\n" + b)

[]
let main argv = 
    diamond |> printfn "%s"
    0

Solution

It's hard to comment on it. It seems OK to me - maybe a little to much list manipulation. I'm not sure if string concat with + is acceptable practice in F# but in general it's not. For fun I've tried with my own version with use of recursion:

let newDiamond n =
    let rec print nn f = 
        if nn  0 then
            let spaces = String.replicate ((n - nn) / 2) " "
            sprintf "%s%s%s\n%s" spaces (String.replicate nn "*") spaces (print (f nn) f)
        else
            ""
    let add x = x + 2
    let subt x = x - 2
    let dn = if n % 2 = 0 then 3 else 2
    sprintf "%s%s" (print 1 add) (print (n - dn) subt)

[]
let main argv = 
    newDiamond 36 |> printfn "%s"    
    Console.ReadLine() |> ignore
    0


Version with pattern match instead of if:

let newDiamond n =
    let rec print nn f = 
        match nn with
        | x when x = n + 1 -> ""
        | _ -> let spaces = String.replicate ((n - nn) / 2) " "
               sprintf "%s%s%s\n%s" spaces (String.replicate nn "*") spaces (print (f nn) f)

    let add x = x + 2
    let subt x = x - 2
    let dn = if n % 2 = 0 then 3 else 2
    sprintf "%s%s" (print 1 add) (print (n - dn) subt)


It's possible but does it add any good?

EDIT:

A condensed solution could be:

let newDiamond n = 
    let topNs = [1..2..n]
    let diamondNs = List.append topNs (topNs |> List.rev |> List.tail)

    let createLine x = 
        sprintf "%s%s" (String.replicate ((n - x) / 2) " ") (String.replicate x "*")

    (diamondNs |> Seq.mapFold (fun strs x -> ("", sprintf "%s\n%s" strs (createLine x))) "") |> snd


However none of the solutions are very performant if diamond size is increased (> 2000) due to the concatenation of strings. In that respect a good old iterative solution is preferable:

let newDiamond n =
    let createLine = (fun x -> sprintf "%s%s" (String.replicate ((n - x) / 2) " ") (String.replicate x "*"))
    let topHalf = [ 1..2..n ]
    let indices = topHalf @ (topHalf |> List.rev |> List.tail)
    seq { for ln in indices do
            yield createLine ln } |> Seq.iter (printfn "%s")


A gentle "refactoring" of BadHeuristics code:

let rangeWithInverse max = 
    List.zip [ 1 .. max ] [ max .. -1 .. 1]

let listAscDesc list = 
    list @ (list |> List.rev |> List.tail) // tail instead of skip 1

let starsAndSpacesString (stars, spaces) =
    sprintf "%s%s" (String.replicate spaces " ") (String.replicate ((stars * 2) - 1) "*") // sprintf instead of ""+""

let diamond n =
    rangeWithInverse (n / 2)
        |> listAscDesc
        |> Seq.map starsAndSpacesString
        |> Seq.reduce (fun a b -> sprintf "%s\n%s" a b) // sprintf instead of ""+""

Code Snippets

let newDiamond n =
    let rec print nn f = 
        if nn <= n && nn > 0 then
            let spaces = String.replicate ((n - nn) / 2) " "
            sprintf "%s%s%s\n%s" spaces (String.replicate nn "*") spaces (print (f nn) f)
        else
            ""
    let add x = x + 2
    let subt x = x - 2
    let dn = if n % 2 = 0 then 3 else 2
    sprintf "%s%s" (print 1 add) (print (n - dn) subt)

[<EntryPoint>]
let main argv = 
    newDiamond 36 |> printfn "%s"    
    Console.ReadLine() |> ignore
    0
let newDiamond n =
    let rec print nn f = 
        match nn with
        | x when x <= 0 || x >= n + 1 -> ""
        | _ -> let spaces = String.replicate ((n - nn) / 2) " "
               sprintf "%s%s%s\n%s" spaces (String.replicate nn "*") spaces (print (f nn) f)

    let add x = x + 2
    let subt x = x - 2
    let dn = if n % 2 = 0 then 3 else 2
    sprintf "%s%s" (print 1 add) (print (n - dn) subt)
let newDiamond n = 
    let topNs = [1..2..n]
    let diamondNs = List.append topNs (topNs |> List.rev |> List.tail)

    let createLine x = 
        sprintf "%s%s" (String.replicate ((n - x) / 2) " ") (String.replicate x "*")

    (diamondNs |> Seq.mapFold (fun strs x -> ("", sprintf "%s\n%s" strs (createLine x))) "") |> snd
let newDiamond n =
    let createLine = (fun x -> sprintf "%s%s" (String.replicate ((n - x) / 2) " ") (String.replicate x "*"))
    let topHalf = [ 1..2..n ]
    let indices = topHalf @ (topHalf |> List.rev |> List.tail)
    seq { for ln in indices do
            yield createLine ln } |> Seq.iter (printfn "%s")
let rangeWithInverse max = 
    List.zip [ 1 .. max ] [ max .. -1 .. 1]

let listAscDesc list = 
    list @ (list |> List.rev |> List.tail) // tail instead of skip 1

let starsAndSpacesString (stars, spaces) =
    sprintf "%s%s" (String.replicate spaces " ") (String.replicate ((stars * 2) - 1) "*") // sprintf instead of ""+""

let diamond n =
    rangeWithInverse (n / 2)
        |> listAscDesc
        |> Seq.map starsAndSpacesString
        |> Seq.reduce (fun a b -> sprintf "%s\n%s" a b) // sprintf instead of ""+""

Context

StackExchange Code Review Q#145384, answer score: 3

Revisions (0)

No revisions yet.