Pemformatan teks biasa

F# mendukung pemformatan teks biasa yang diperiksa jenisnya menggunakan printf, printfn, sprintf, dan fungsi terkait. Contohnya,

dotnet fsi

> printfn "Hello %s, %d + %d is %d" "world" 2 2 (2+2);;

memberikan output

Hello world, 2 + 2 is 4

F# juga memungkinkan nilai terstruktur untuk diformat sebagai teks biasa. Misalnya, perhatikan contoh berikut yang memformat output sebagai tampilan tuple seperti matriks.

dotnet fsi

> printfn "%A" [ for i in 1 .. 5 -> [ for j in 1 .. 5 -> (i, j) ] ];;

[[(1, 1); (1, 2); (1, 3); (1, 4); (1, 5)];
 [(2, 1); (2, 2); (2, 3); (2, 4); (2, 5)];
 [(3, 1); (3, 2); (3, 3); (3, 4); (3, 5)];
 [(4, 1); (4, 2); (4, 3); (4, 4); (4, 5)];
 [(5, 1); (5, 2); (5, 3); (5, 4); (5, 5)]]

Pemformatan teks biasa terstruktur diaktifkan jika Anda menggunakan format %A dalam string pemformatan printf. Ini juga diaktifkan saat memformat output nilai dalam interaktif F#, yang output-nya menyertakan informasi tambahan dan juga dapat disesuaikan. Pemformatan teks biasa juga dapat diamati melalui panggilan apa pun ke x.ToString() pada nilai rekaman dan gabungan F#, termasuk yang terjadi secara implisit dalam penelusuran kesalahan, pengelogan, dan peralatan lainnya.

Memeriksa string format-printf

Kesalahan waktu kompilasi akan dilaporkan jika fungsi pemformatan printf digunakan dengan argumen yang tidak cocok dengan penentu format printf dalam string format. Contohnya,

sprintf "Hello %s" (2+2)

memberikan output

  sprintf "Hello %s" (2+2)
  ----------------------^

stdin(3,25): error FS0001: The type 'string' does not match the type 'int'

Secara teknis, saat menggunakan printf dan fungsi terkait lainnya, aturan khusus dalam kompilator F# memeriksa harfiah string yang diteruskan sebagai string format, memastikan argumen berikutnya yang diterapkan adalah jenis yang benar agar sesuai dengan penentu format yang digunakan.

Penentu format untuk printf

Spesifikasi format untuk format printf adalah string dengan penanda % yang menunjukkan format. Tempat penampung format terdiri dari %[flags][width][.precision][type] yang jenisnya ditafsirkan sebagai berikut:

Penentu format Jenis Keterangan
%b boolAku akan menemuinya.System.Boolean Diformat sebagai true atau false
%s stringAku akan menemuinya.System.String Diformat sebagai isinya yang tidak lolos
%c charAku akan menemuinya.System.Char Diformat sebagai karakter harfiah
%d, %i jenis bilangan bulat dasar Diformat sebagai bilangan bulat desimal, ditandatangani jika jenis bilangan bulat dasar ditandatangani
%u jenis bilangan bulat dasar Diformat sebagai bilangan bulat desimal tidak bertanda
%x, %X jenis bilangan bulat dasar Diformat sebagai angka heksadesimal tidak bertanda (a-f atau A-F untuk masing-masing digit heksa)
%o jenis bilangan bulat dasar Diformat sebagai bilangan oktal tidak bertanda
%B jenis bilangan bulat dasar Diformat sebagai bilangan biner tidak bertanda
%e, %E jenis floating point dasar Diformat sebagai nilai bertanda yang memiliki bentuk [-]d.dddde[sign]ddd di mana d adalah satu digit desimal, dddd adalah satu atau lebih digit desimal, ddd tepat tiga digit desimal, dan tanda adalah + atau -
%f, %F jenis floating point dasar Diformat sebagai nilai bertanda yang memiliki bentuk [-]dddd.dddd, di mana dddd adalah satu atau lebih digit desimal. Jumlah digit sebelum titik desimal tergantung pada besarnya angka, dan jumlah digit setelah titik desimal tergantung pada presisi yang diminta.
%g, %G jenis floating point dasar Diformat menggunakan sebagai nilai bertanda yang dicetak dalam format %f atau %e, mana saja yang lebih ringkas untuk nilai dan presisi yang diberikan.
%M nilai decimal (System.Decimal) Diformat menggunakan penentu format "G" untuk System.Decimal.ToString(format)
%O nilai apa pun Diformat dengan boxing objek dan memanggil metode System.Object.ToString()-nya
%A nilai apa pun Diformat menggunakan pemformatan teks biasa terstruktur dengan pengaturan tata letak default
%a nilai apa pun Memerlukan dua argumen: fungsi pemformatan yang menerima parameter konteks dan nilainya, dan nilai tertentu untuk dicetak
%t nilai apa pun Memerlukan satu argumen: fungsi pemformatan yang menerima parameter konteks yang menghasilkan atau mengembalikan teks yang sesuai
%% (tidak ada) Tidak memerlukan argumen dan mencetak tanda persen biasa: %

Jenis bilangan bulat dasar adalah byte (System.Byte), sbyte (System.SByte), int16 (System.Int16), uint16 (System.UInt16), int32 (System.Int32), uint32 (System.UInt32), int64 (System.Int64), uint64 (System.UInt64), nativeint (System.IntPtr), dan unativeint (System.UIntPtr). Jenis floating point dasar adalah float (System.Double), float32 (System.Single), dan decimal (System.Decimal).

Lebar opsional adalah bilangan bulat yang menunjukkan lebar minimal dari hasil. Misalnya, %6d mencetak bilangan bulat, mengawalinya dengan spasi untuk mengisi setidaknya enam karakter. Jika lebar adalah *, argumen bilangan bulat tambahan diambil untuk menentukan lebar yang sesuai.

Bendera yang valid adalah:

Bendera Efek
0 Menambahkan nol sebagai ganti dari spasi untuk membuat lebar yang diperlukan
- Rata kiri hasil dalam lebar yang ditentukan
+ Menambahkan karakter + jika angkanya positif (untuk mencocokkan tanda - untuk negatif)
karakter spasi Menambahkan spasi tambahan jika angkanya positif (untuk mencocokkan tanda '-' untuk negatif)

Bendera printf # tidak valid dan kesalahan waktu kompilasi akan dilaporkan jika digunakan.

Nilai diformat menggunakan kultur invarian. Pengaturan kultur tidak relevan dengan pemformatan printf kecuali jika memengaruhi hasil pemformatan %O dan %A. Untuk informasi selengkapnya, lihat pemformatan teks biasa terstruktur.

Pemformatan %A

Penentu format %A digunakan untuk memformat nilai dengan cara yang dapat dibaca manusia, dan juga dapat berguna untuk melaporkan informasi diagnostik.

Nilai primitif

Saat memformat teks biasa menggunakan penentu %A, nilai numerik F# diformat dengan sufiks dan kultur invarian. Nilai floating point diformat menggunakan 10 tempat presisi floating point. Contohnya,

printfn "%A" (1L, 3n, 5u, 7, 4.03f, 5.000000001, 5.0000000001)

menghasilkan

(1L, 3n, 5u, 7, 4.03000021f, 5.000000001, 5.0)

Saat menggunakan penentu %A, string diformat menggunakan tanda kutip. Kode escape tidak ditambahkan dan sebagai gantinya karakter mentah dicetak. Contohnya,

printfn "%A" ("abc", "a\tb\nc\"d")

menghasilkan

("abc", "a      b
c"d")

Nilai .NET

Saat memformat teks biasa menggunakan penentu %A, objek .NET non-F# diformat dengan menggunakan x.ToString() menggunakan pengaturan default .NET yang diberikan oleh System.Globalization.CultureInfo.CurrentCulture dan System.Globalization.CultureInfo.CurrentUICulture. Contohnya,

open System.Globalization

let date = System.DateTime(1999, 12, 31)

CultureInfo.CurrentCulture <- CultureInfo.GetCultureInfo("de-DE")
printfn "Culture 1: %A" date

CultureInfo.CurrentCulture <- CultureInfo.GetCultureInfo("en-US")
printfn "Culture 2: %A" date

menghasilkan

Culture 1: 31.12.1999 00:00:00
Culture 2: 12/31/1999 12:00:00 AM

Nilai terstruktur

Saat memformat teks biasa menggunakan penentu %A, indentasi blok digunakan untuk daftar dan tuple F#. Ini ditunjukkan dalam contoh sebelumnya. Struktur array juga digunakan, termasuk array multidimensi. Array satu dimensi ditampilkan dengan sintaksis [| ... |]. Contohnya,

printfn "%A" [| for i in 1 .. 20 -> (i, i*i) |]

menghasilkan

[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25); (6, 36); (7, 49); (8, 64); (9, 81);
  (10, 100); (11, 121); (12, 144); (13, 169); (14, 196); (15, 225); (16, 256);
  (17, 289); (18, 324); (19, 361); (20, 400)|]

Lebar cetak default adalah 80. Lebar ini dapat disesuaikan dengan menggunakan lebar cetak dalam penentu format. Contohnya,

printfn "%10A" [| for i in 1 .. 5 -> (i, i*i) |]

printfn "%20A" [| for i in 1 .. 5 -> (i, i*i) |]

printfn "%50A" [| for i in 1 .. 5 -> (i, i*i) |]

menghasilkan

[|(1, 1);
  (2, 4);
  (3, 9);
  (4, 16);
  (5, 25)|]
[|(1, 1); (2, 4);
  (3, 9); (4, 16);
  (5, 25)|]
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]

Menentukan lebar cetak 0 menghasilkan tidak ada lebar cetak yang digunakan. Satu baris teks akan dihasilkan, kecuali string yang disematkan dalam output berisi jeda baris. Misalnya

printfn "%0A" [| for i in 1 .. 5 -> (i, i*i) |]

printfn "%0A" [| for i in 1 .. 5 -> "abc\ndef" |]

menghasilkan

[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]
[|"abc
def"; "abc
def"; "abc
def"; "abc
def"; "abc
def"|]

Batas kedalaman 4 digunakan untuk nilai urutan (IEnumerable), yang ditampilkan sebagai seq { ...}. Batas kedalaman 100 digunakan untuk nilai daftar dan array. Contohnya,

printfn "%A" (seq { for i in 1 .. 10 -> (i, i*i) })

menghasilkan

seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]

Indentasi blok juga digunakan untuk struktur rekaman publik dan nilai gabungan. Contohnya,

type R = { X : int list; Y : string list }

printfn "%A" { X =  [ 1;2;3 ]; Y = ["one"; "two"; "three"] }

menghasilkan

{ X = [1; 2; 3]
  Y = ["one"; "two"; "three"] }

Jika %+A digunakan, struktur privat dari rekaman dan gabungan juga diungkapkan dengan menggunakan refleksi. Misalnya

type internal R =
    { X : int list; Y : string list }
    override _.ToString() = "R"

let internal data = { X = [ 1;2;3 ]; Y = ["one"; "two"; "three"] }

printfn "external view:\n%A" data

printfn "internal view:\n%+A" data

menghasilkan

external view:
R

internal view:
{ X = [1; 2; 3]
  Y = ["one"; "two"; "three"] }

Nilai besar, siklik, atau sangat berlapis

Nilai terstruktur besar diformat ke jumlah node objek keseluruhan maksimum 10000. Nilai yang sangat berlapis diformat hingga kedalaman 100. Dalam kedua kasus ... digunakan untuk menghilangkan beberapa output. Contohnya,

type Tree =
    | Tip
    | Node of Tree * Tree

let rec make n =
    if n = 0 then
        Tip
    else
        Node(Tip, make (n-1))

printfn "%A" (make 1000)

menghasilkan output yang besar dengan beberapa bagian yang dihilangkan:

Node(Tip, Node(Tip, ....Node (..., ...)...))

Siklus terdeteksi dalam grafik objek dan ... digunakan di tempat yang siklusnya terdeteksi. Misalnya

type R = { mutable Links: R list }
let r = { Links = [] }
r.Links <- [r]
printfn "%A" r

menghasilkan

{ Links = [...] }

Nilai lazy, null, dan fungsi

Nilai lazy dicetak sebagai Value is not created atau teks yang setara jika nilainya belum dievaluasi.

Nilai null dicetak sebagai null kecuali jika jenis nilai statik ditentukan sebagai jenis gabungan di mana null adalah representasi yang diizinkan.

Nilai fungsi F# dicetak sebagai nama penutupan yang dibuat secara internal, misalnya, <fun:it@43-7>.

Menyesuaikan pemformatan teks biasa dengan StructuredFormatDisplay

Saat menggunakan penentu %A, keberadaan atribut StructuredFormatDisplay pada deklarasi jenis akan dipatuhi. Ini dapat digunakan untuk menentukan teks pengganti dan properti untuk menampilkan nilai. Misalnya:

[<StructuredFormatDisplay("Counts({Clicks})")>]
type Counts = { Clicks:int list}

printfn "%20A" {Clicks=[0..20]}

menghasilkan

Counts([0; 1; 2; 3;
        4; 5; 6; 7;
        8; 9; 10; 11;
        12; 13; 14;
        15; 16; 17;
        18; 19; 20])

Menyesuaikan pemformatan teks biasa dengan mengambil alih ToString

Penerapan default dari ToString dapat diamati dalam pemrograman F#. Seringkali, hasil default tidak cocok untuk digunakan baik dalam tampilan informasi yang berhubungan dengan programmer atau output pengguna, dan akibatnya adalah hal yang umum untuk mengambil alih penerapan default.

Secara default, jenis rekaman dan gabungan F# mengambil alih penerapan ToString dengan penerapan yang menggunakan sprintf "%+A". Contohnya,

type Counts = { Clicks:int list }

printfn "%s" ({Clicks=[0..10]}.ToString())

menghasilkan

{ Clicks = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10] }

Untuk jenis kelas, tidak ada penerapan default ToString yang disediakan dan default .NET digunakan, yang melaporkan nama jenis. Contohnya,

type MyClassType(clicks: int list) =
   member _.Clicks = clicks

let data = [ MyClassType([1..5]); MyClassType([1..5]) ]
printfn "Default structured print gives this:\n%A" data
printfn "Default ToString gives:\n%s" (data.ToString())

menghasilkan

Default structured print gives this:
[MyClassType; MyClassType]
Default ToString gives:
[MyClassType; MyClassType]

Menambahkan pengambilalihan untuk ToString dapat memberikan pemformatan yang lebih baik.

type MyClassType(clicks: int list) =
   member _.Clicks = clicks
   override _.ToString() = sprintf "MyClassType(%0A)" clicks

let data = [ MyClassType([1..5]); MyClassType([1..5]) ]
printfn "Now structured print gives this:\n%A" data
printfn "Now ToString gives:\n%s" (data.ToString())

menghasilkan

Now structured print gives this:
[MyClassType([1; 2; 3; 4; 5]); MyClassType([1; 2; 3; 4; 5])]
Now ToString gives:
[MyClassType([1; 2; 3; 4; 5]); MyClassType([1; 2; 3; 4; 5])]

Menyesuaikan pemformatan teks biasa dengan StructuredFormatDisplay dan ToString

Untuk mencapai pemformatan yang konsisten untuk penentu format %A dan %O, gabungkan penggunaan StructuredFormatDisplay dengan pengambilalihan ToString. Contohnya,

[<StructuredFormatDisplay("{DisplayText}")>]
type MyRecord =
    {
        a: int
    }
    member this.DisplayText = this.ToString()

    override _.ToString() = "Custom ToString"

Mengevaluasi definisi berikut

let myRec = { a = 10 }
let myTuple = (myRec, myRec)
let s1 = sprintf $"{myRec}"
let s2 = sprintf $"{myTuple}"
let s3 = sprintf $"%A{myTuple}"
let s4 = sprintf $"{[myRec; myRec]}"
let s5 = sprintf $"%A{[myRec; myRec]}"

memberikan teks

val myRec: MyRecord = Custom ToString
val myTuple: MyRecord * MyRecord = (Custom ToString, Custom ToString)
val s1: string = "Custom ToString"
val s2: string = "(Custom ToString, Custom ToString)"
val s3: string = "(Custom ToString, Custom ToString)"
val s4: string = "[Custom ToString; Custom ToString]"
val s5: string = "[Custom ToString; Custom ToString]"

Penggunaan StructuredFormatDisplay dengan properti DisplayText pendukung berarti fakta bahwa myRec adalah jenis rekaman struktural diabaikan selama pencetakan terstruktur, dan pengambilalihan ToString() lebih disukai dalam semua keadaan.

Penerapan antarmuka System.IFormattable dapat ditambahkan untuk penyesuaian lebih lanjut dengan adanya spesifikasi format .NET.

Pencetakan terstruktur F# Interactive

F# Interactive (dotnet fsi) menggunakan versi ekstensi dari pemformatan teks biasa terstruktur untuk melaporkan nilai dan memungkinkan penyesuaian tambahan. Untuk informasi selengkapnya, lihat F# Interactive.

Menyesuaikan tampilan debug

Debugger untuk .NET mematuhi penggunaan atribut seperti DebuggerDisplay dan DebuggerTypeProxy, dan ini memengaruhi tampilan terstruktur objek di jendela pemeriksaan debugger. Kompilator F# secara otomatis menghasilkan atribut ini untuk jenis gabungan dan rekaman terdiskriminasi, tetapi bukan jenis kelas, antarmuka, atau struktur.

Atribut ini diabaikan dalam pemformatan teks biasa F#, tetapi akan berguna untuk menerapkan metode ini untuk meningkatkan tampilan saat men-debug jenis F#.

Lihat juga