Formatowanie zwykłego tekstu
Język F# obsługuje formatowanie sprawdzane przez typ zwykłego tekstu przy użyciu printf
funkcji , printfn
, sprintf
i powiązanych.
Przykład:
dotnet fsi
> printfn "Hello %s, %d + %d is %d" "world" 2 2 (2+2);;
udostępnia dane wyjściowe
Hello world, 2 + 2 is 4
Język F# umożliwia również formatowanie wartości strukturalnych jako zwykłego tekstu. Rozważmy na przykład następujący przykład, który formatuje dane wyjściowe jako macierzowy wyświetlacz krotki.
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)]]
Formatowanie zwykłego tekstu ze strukturą jest aktywowane w przypadku używania %A
formatu w printf
ciągach formatowania.
Jest on również aktywowany podczas formatowania danych wyjściowych wartości w interaktywnym języku F#, gdzie dane wyjściowe zawierają dodatkowe informacje i można je dodatkowo dostosowywać.
Formatowanie zwykłego tekstu można również zaobserwować za pomocą wywołań w x.ToString()
unii i wartości rekordów języka F#, w tym tych, które występują niejawnie podczas debugowania, rejestrowania i innych narzędzi.
printf
Sprawdzanie ciągów -format
Błąd czasu kompilacji zostanie zgłoszony, jeśli printf
funkcja formatowania jest używana z argumentem, który nie pasuje do specyfikatorów formatu printf w ciągu formatu. Przykład:
sprintf "Hello %s" (2+2)
udostępnia dane wyjściowe
sprintf "Hello %s" (2+2)
----------------------^
stdin(3,25): error FS0001: The type 'string' does not match the type 'int'
Technicznie rzecz biorąc, jeśli używasz printf
i innych powiązanych funkcji, specjalna reguła w kompilatorze języka F# sprawdza literał ciągu przekazany jako ciąg formatu, zapewniając, że kolejne zastosowane argumenty są poprawnego typu, aby dopasować używane specyfikatory formatu.
Specyfikatory formatu dla printf
Specyfikacje formatu dla printf
formatów to ciągi ze %
znacznikami wskazującymi format. Symbole zastępcze formatu składają się z %[flags][width][.precision][type]
tego, gdzie typ jest interpretowany w następujący sposób:
Specyfikator formatu | Typ(-y) | Uwagi |
---|---|---|
%b |
bool (System.Boolean ) |
Sformatowane jako true lub false |
%s |
string (System.String ) |
Sformatowany jako jego niezaobejmowana zawartość |
%c |
char (System.Char ) |
Sformatowany jako literał znaku |
%d , %i |
podstawowy typ liczby całkowitej | Sformatowany jako liczba całkowita dziesiętna, podpisany, jeśli jest podpisany podstawowy typ liczby całkowitej |
%u |
podstawowy typ liczby całkowitej | Sformatowane jako niepodpisana liczba całkowita dziesiętna |
%x , %X |
podstawowy typ liczby całkowitej | Sformatowany jako niepodpisany numer szesnastkowy (odpowiednio cyfry szesnastkowe a-f lub A-F) |
%o |
podstawowy typ liczby całkowitej | Sformatowany jako niepodpisany numer ósemkowy |
%B |
podstawowy typ liczby całkowitej | Sformatowany jako niepodpisany numer binarny |
%e , %E |
podstawowy typ zmiennoprzecinkowa | Sformatowany jako wartość ze znakiem o formularzu [-]d.dddde[sign]ddd , w którym d jest pojedynczą cyfrą dziesiętną, dddd jest co najmniej jedną cyfrą dziesiętną, ddd jest dokładnie trzy cyfry dziesiętne, a znak jest + lub - |
%f , %F |
podstawowy typ zmiennoprzecinkowa | Sformatowany jako wartość ze znakiem o formularzu [-]dddd.dddd , gdzie dddd jest co najmniej jedną cyfrą dziesiętną. Liczba cyfr przed punktem dziesiętnym zależy od wielkości liczby, a liczba cyfr po przecinku dziesiętnym zależy od żądanej precyzji. |
%g , %G |
podstawowy typ zmiennoprzecinkowa | Sformatowane przy użyciu wartości podpisanej wydrukowanej w formacie lub %e w %f zależności od tego, która wartość jest bardziej kompaktowa dla danej wartości i precyzji. |
%M |
a decimal (System.Decimal ) wartość |
Sformatowane przy użyciu specyfikatora "G" formatu dla System.Decimal.ToString(format) |
%O |
dowolna wartość | Sformatowane przez pole obiektu i wywoływanie jego System.Object.ToString() metody |
%A |
dowolna wartość | Sformatowane przy użyciu formatowania zwykłego tekstu ze strukturą z domyślnymi ustawieniami układu |
%a |
dowolna wartość | Wymaga dwóch argumentów: funkcja formatowania akceptująca parametr kontekstu i wartość oraz konkretną wartość do wydrukowania |
%t |
dowolna wartość | Wymaga jednego argumentu: funkcja formatowania akceptująca parametr kontekstu, który zwraca lub zwraca odpowiedni tekst |
%% |
(brak) | Nie wymaga żadnych argumentów i drukuje zwykły znak procentu: % |
Podstawowe typy liczb całkowitych to byte
(System.Byte
), sbyte
(System.SByte
), int16
System.Int16
(), uint16
(System.UInt16
), (), int32
(System.Int32
), uint32
(System.Int64
System.UInt64
int64
System.UInt32
nativeint
System.IntPtr
uint64
) i unativeint
().System.UIntPtr
Podstawowe typy zmiennoprzecinkowe to float
(System.Double
), float32
(System.Single
) i decimal
(System.Decimal
).
Opcjonalna szerokość to liczba całkowita wskazująca minimalną szerokość wyniku. Na przykład %6d
drukuje liczbę całkowitą, prefiksując ją spacjami, aby wypełnić co najmniej sześć znaków. Jeśli szerokość to *
, zostanie pobrany dodatkowy argument liczby całkowitej, aby określić odpowiednią szerokość.
Prawidłowe flagi to:
Flaga | Efekt |
---|---|
0 |
Dodaj zera zamiast spacji, aby utworzyć wymaganą szerokość |
- |
W lewo uzasadniaj wynik w określonej szerokości |
+ |
+ Dodaj znak, jeśli liczba jest dodatnia (aby dopasować - znak dla wartości ujemnych) |
znak spacji | Dodaj dodatkowe miejsce, jeśli liczba jest dodatnia (aby dopasować znak "-" dla wartości ujemnych) |
Flaga printf #
jest nieprawidłowa i zostanie zgłoszony błąd czasu kompilacji, jeśli jest używany.
Wartości są formatowane przy użyciu niezmiennej kultury. Ustawienia kultury nie mają znaczenia dla printf
formatowania, z wyjątkiem sytuacji, gdy mają wpływ na wyniki %O
i %A
formatowanie. Aby uzyskać więcej informacji, zobacz Formatowanie zwykłego tekstu ze strukturą.
%A
Formatowania
Specyfikator %A
formatu służy do formatowania wartości w sposób czytelny dla człowieka i może być również przydatny w przypadku raportowania informacji diagnostycznych.
Wartości pierwotne
Podczas formatowania zwykłego %A
tekstu przy użyciu specyfikatora wartości liczbowe języka F# są formatowane z sufiksem i niezmienną kulturą. Wartości zmiennoprzecinkowe są formatowane przy użyciu 10 miejsc precyzji zmiennoprzecinkowych. Przykład:
printfn "%A" (1L, 3n, 5u, 7, 4.03f, 5.000000001, 5.0000000001)
Produkuje
(1L, 3n, 5u, 7, 4.03000021f, 5.000000001, 5.0)
W przypadku korzystania z specyfikatora %A
ciągi są formatowane przy użyciu cudzysłowów. Kody ucieczki nie są dodawane i zamiast tego są drukowane nieprzetworzone znaki. Przykład:
printfn "%A" ("abc", "a\tb\nc\"d")
Produkuje
("abc", "a b
c"d")
Wartości platformy .NET
Podczas formatowania zwykłego %A
tekstu przy użyciu specyfikatora obiekty inne niż F# platformy .NET są formatowane przy użyciu x.ToString()
domyślnych ustawień platformy .NET podanych przez System.Globalization.CultureInfo.CurrentCulture
i System.Globalization.CultureInfo.CurrentUICulture
. Przykład:
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
Produkuje
Culture 1: 31.12.1999 00:00:00
Culture 2: 12/31/1999 12:00:00 AM
Wartości ustrukturyzowane
Podczas formatowania zwykłego tekstu przy użyciu specyfikatora %A
wcięcie bloku jest używane dla list i krotek języka F#. Jest to pokazane w poprzednim przykładzie.
Używana jest również struktura tablic, w tym tablic wielowymiarowych. Tablice jednowymiarowe są wyświetlane ze składnią [| ... |]
. Przykład:
printfn "%A" [| for i in 1 .. 20 -> (i, i*i) |]
Produkuje
[|(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)|]
Domyślna szerokość wydruku to 80. Tę szerokość można dostosować przy użyciu szerokości wydruku w specyfikatorze formatu. Przykład:
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) |]
Produkuje
[|(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)|]
Określenie szerokości wydruku 0 powoduje, że nie jest używana szerokość wydruku. Zostanie utworzony pojedynczy wiersz tekstu, z wyjątkiem sytuacji, w których osadzone ciągi w danych wyjściowych zawierają podziały wierszy. Na przykład
printfn "%0A" [| for i in 1 .. 5 -> (i, i*i) |]
printfn "%0A" [| for i in 1 .. 5 -> "abc\ndef" |]
Produkuje
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]
[|"abc
def"; "abc
def"; "abc
def"; "abc
def"; "abc
def"|]
Limit głębokości 4 jest używany dla wartości sekwencji (IEnumerable
), które są wyświetlane jako seq { ...}
. Limit głębokości 100 jest używany dla wartości listy i tablicy.
Przykład:
printfn "%A" (seq { for i in 1 .. 10 -> (i, i*i) })
Produkuje
seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]
Wcięcie bloku jest również używane dla struktury wartości rekordów publicznych i unii. Przykład:
type R = { X : int list; Y : string list }
printfn "%A" { X = [ 1;2;3 ]; Y = ["one"; "two"; "three"] }
Produkuje
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
Jeśli %+A
jest używany, prywatna struktura rekordów i związków jest również ujawniana przy użyciu odbicia. Na przykład
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
Produkuje
external view:
R
internal view:
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
Duże, cykliczne lub głęboko zagnieżdżone wartości
Duże wartości ustrukturyzowane są sformatowane do maksymalnej ogólnej liczby węzłów obiektu 10000.
Głęboko zagnieżdżone wartości są formatowane na głębokość 100. W obu przypadkach ...
służy do elide niektórych danych wyjściowych. Przykład:
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)
generuje duże dane wyjściowe z niektórymi częściami elided:
Node(Tip, Node(Tip, ....Node (..., ...)...))
Cykle są wykrywane w grafach obiektów i ...
są używane w miejscach, w których wykryto cykle. Na przykład
type R = { mutable Links: R list }
let r = { Links = [] }
r.Links <- [r]
printfn "%A" r
Produkuje
{ Links = [...] }
Wartości z opóźnieniem, null i funkcji
Wartości z opóźnieniem są drukowane jako Value is not created
tekst lub równoważne, gdy wartość nie została jeszcze obliczona.
Wartości null są drukowane jako null
, chyba że statyczny typ wartości jest określany jako typ unii, gdzie null
jest dozwoloną reprezentacją.
Wartości funkcji języka F# są drukowane jako ich wewnętrznie wygenerowana nazwa zamknięcia, na przykład <fun:it@43-7>
.
Dostosowywanie formatowania zwykłego tekstu za pomocą polecenia StructuredFormatDisplay
W przypadku używania %A
specyfikatora obecność atrybutu StructuredFormatDisplay
w deklaracjach typów jest przestrzegana. Może to służyć do określania zastępczego tekstu i właściwości w celu wyświetlenia wartości. Na przykład:
[<StructuredFormatDisplay("Counts({Clicks})")>]
type Counts = { Clicks:int list}
printfn "%20A" {Clicks=[0..20]}
Produkuje
Counts([0; 1; 2; 3;
4; 5; 6; 7;
8; 9; 10; 11;
12; 13; 14;
15; 16; 17;
18; 19; 20])
Dostosowywanie formatowania zwykłego tekstu przez zastąpienie ToString
Domyślna implementacja ToString
programu jest zauważalna w programowaniu języka F#. Często domyślne wyniki nie są odpowiednie do użycia w wyświetlaniu informacji programisty lub danych wyjściowych użytkownika, a w związku z tym często przesłaniają domyślną implementację.
Domyślnie typy rekordów ToString
i unii języka F# zastępują implementację za pomocą implementacji używającej polecenia sprintf "%+A"
. Przykład:
type Counts = { Clicks:int list }
printfn "%s" ({Clicks=[0..10]}.ToString())
Produkuje
{ Clicks = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10] }
W przypadku typów klas nie podano domyślnej implementacji ToString
programu i jest używana wartość domyślna platformy .NET, która zgłasza nazwę typu. Przykład:
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())
Produkuje
Default structured print gives this:
[MyClassType; MyClassType]
Default ToString gives:
[MyClassType; MyClassType]
Dodanie przesłonięcia dla ToString
elementu może zapewnić lepsze formatowanie.
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())
Produkuje
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])]
Dostosowywanie formatowania zwykłego tekstu za pomocą polecenia StructuredFormatDisplay
i ToString
Aby uzyskać spójne formatowanie dla %A
specyfikatorów formatu i %O
, połącz użycie funkcji StructuredFormatDisplay
z przesłonięciem ToString
elementu . Przykład:
[<StructuredFormatDisplay("{DisplayText}")>]
type MyRecord =
{
a: int
}
member this.DisplayText = this.ToString()
override _.ToString() = "Custom ToString"
Ocenianie następujących definicji
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]}"
wyświetla tekst
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]"
Użycie StructuredFormatDisplay
z właściwością nośną DisplayText
oznacza fakt, że myRec
jest to typ rekordu strukturalnego jest ignorowany podczas drukowania strukturalnego, a zastąpienie elementu jest preferowane ToString()
we wszystkich okolicznościach.
Implementację interfejsu System.IFormattable
można dodać do dalszego dostosowywania w przypadku specyfikacji formatu platformy .NET.
Interaktywne drukowanie strukturalne języka F#
Język F# Interactive (dotnet fsi
) używa rozszerzonej wersji formatowania zwykłego tekstu do wartości raportu i umożliwia dodatkowe dostosowywanie. Aby uzyskać więcej informacji, zobacz F# Interactive.
Dostosowywanie wyświetlania debugowania
Debugery dla platformy .NET szanują użycie atrybutów, takich jak DebuggerDisplay
i DebuggerTypeProxy
, i mają wpływ na ustrukturyzowane wyświetlanie obiektów w oknach inspekcji debugera.
Kompilator języka F# automatycznie wygenerował te atrybuty dla typów związków i rekordów dyskryminowanych, ale nie klas, interfejsów lub typów struktur.
Te atrybuty są ignorowane w formatowaniu zwykłego tekstu języka F#, ale może być przydatne zaimplementowanie tych metod w celu ulepszenia wyświetlania podczas debugowania typów języka F#.