Formatting the multiplication table f #

I am trying to create an N x M multiplication table program using f # where the values ​​of n and m are given by the user and the table is calculated and stored in a 2D array and displayed on the console.

Any help would be very grateful for my array to display in a format like:

Desired Output

unlike the one in my program

Current Output

My source code

open System

let func() =
        let multiplication  n m = Array2D.init n m (fun n m -> n * m)


        printf "Enter N Value: "
        let nValue = Console.ReadLine() |> System.Int32.Parse

        printf "\nEnter M Value: "
        let mValue = Console.ReadLine() |> System.Int32.Parse

        let grid = multiplication nValue mValue 


        printfn "\n\n%A" grid

func()

      

Also I would like to know how I can get my values ​​to start at 1 and not 0.

Any help would be much appreciated as I am new to F #.

+3


source to share


3 answers


All you have to do is add 1 to n and m before concatenating them, for example

let multiplication  n m = Array2D.init n m (fun n m -> (n + 1) * (m + 1))

      

However, we have some parenthesis craziness, you can refactor it like this:



let myMultFunction n m = (n + 1) * (m + 1)
let multiplication n m = Array2D.init n m myMultFunction

      

The formatting is going to be a bit tricky, and using for loops is a bit of trickery, not very F #, but given that we are using 2d arrays, which are not functional in nature, I figured I could sneak it :;

printfn "A multiplication table:"
printf "   "
for col in 0 .. mValue - 1 do
    printf "%d\t" (col + 1)

printfn ""
for row in 0 .. nValue - 1 do
    for col in 0 .. mValue - 1 do
            if col = 0 then
                    printf "\n%d| " (row + 1)
            printf "%d\t" grid.[row, col]

      

+3


source


Formatting the output, like any user interface task, is usually surprisingly difficult. This case is no exception.

The idea would be like this:

  • Specify how wide the "cells" of the table should be.
  • Plot each row by concatenating the numbers converted to a string and appended to the cell width.
  • Prepare the first line.
  • Concatenate all lines separating them with a newline character.

First, let's see how we can determine the width of the "cell". What is the widest number in the table? Assuming both n

and are m

positive, the widest number will be n*m

obvious. So, we can calculate the cell width as follows:

let cellWidth = (n*m) |> string |> String.length

      

Likewise, the very first (left-most) column would be as wide as the largest number in it, this is n

:

let firstColWidth = n |> string |> String.length

      

Now let's make ourselves a function that will accept a number and a left panel with spaces to the required width:

let pad totalWidth (value: obj) = 
    let s = string value
    if s.Length >= totalWidth then s
    else (String.replicate (totalWidth-s.Length) " ") + s

      

This function is easy to follow: if the line is already exceeded, just return it, otherwise add spaces (totalWidth-s.Length)

.



With this function, we can format the row of our grid:

let formatRow rowIdx =
    let cells = [for colIdx in 0..m-1 -> grid.[rowIdx,colIdx] |> pad cellWidth] // Each cell in this row padded to `cellWidth`
    let firstCol = (rowIdx+1) |> pad firstColWidth  // Leftmost column - just the row index itself padded to `firstColWidth`
    let wholeRow = firstCol :: cells  // Whole row consists of the leftmost column plus subsequent cells
    String.concat " " wholeRow

      

Likewise, format the top-most line:

let firstRow = 
    let cols = [for col in 1..m -> col |> pad cellWidth]
    let firstCol = " " |> pad firstColWidth
    let wholeRow = firstCol :: cols
    String.concat " " wholeRow

      

See how similar these functions are: the only difference is grid.[rowIdx,colIdx]

vs. col

... Why don't we generalize this?

let formatRowWith firstCell getCell =
    let cells = [for colIdx in 0..m-1 -> getCell colIdx |> pad cellWidth]
    let firstCol = firstCell |> pad firstColWidth
    let wholeRow = firstCol :: cells
    String.concat " " wholeRow

let formatRow rowIdx = formatRowWith (rowIdx+1) (fun c -> grid.[rowIdx,c])
let firstRow = formatRowWith " " (fun c -> c+1)

      

Finally, format each line, add the first and concatenate them all:

let rows = [0..n-1] |> List.map formatRow
let allRows = firstRow :: rows
String.concat "\n" allRows

      

Final code:

let formatGrid (grid:_[,]) =
    let n, m = grid.GetLength 0, grid.GetLength 1
    let cellWidth = (n*m) |> string |> String.length
    let firstColWidth = n |> string |> String.length

    let pad totalWidth (value: obj) = 
        let s = string value
        if s.Length >= totalWidth then s
        else (String.replicate (totalWidth-s.Length) " ") + s

    let formatRowWith firstCell getCell =
        let cells = [for colIdx in 0..m-1 -> getCell colIdx |> pad cellWidth]
        let firstCol = firstCell |> pad firstColWidth
        let wholeRow = firstCol :: cells
        String.concat " " wholeRow

    let formatRow rowIdx = formatRowWith (rowIdx+1) (fun c -> grid.[rowIdx,c])
    let firstRow = formatRowWith " " id

    let rows = [0..n-1] |> List.map formatRow
    let allRows = firstRow :: rows
    String.concat "\n" allRows

      

+5


source


I shamelessly copied some parts of Fyodor Soikin's excellent answer, but changed the remainder to make the code more "functional" and shorter, albeit less understandable for those with an "imperative" bent.

let pad totalWidth (value: obj) = 
    let s = string value
    if s.Length >= totalWidth then s
    else (String.replicate (totalWidth - s.Length) " ") + s

let formatGrid (grid:_[,]) =
    let n, m = grid.GetLength 0, grid.GetLength 1
    let cellWidth = (n*m) |> string |> String.length
    let firstColWidth = n |> string |> String.length

    let frow str (rw: int []) = (Array.fold (fun s i -> s + (pad cellWidth i)) str rw) + "\n"
    let firstRow = frow ("\n" + pad (firstColWidth+2) "") [|1..m|]
    let folder str i = str + (frow ((pad firstColWidth (i+1)) + "| ") grid.[i, 0..])

    List.fold folder firstRow [0..(n-1)]

let a2d = array2D [[1;2;3]; [4;5;6]]
formatGrid a2d

      

Output:

val it : string = "
   123
1| 123
2| 456
"

      

0


source







All Articles