Opmaak van tekst zonder opmaak

F# ondersteunt door het type gecontroleerd opmaak van tekst zonder opmaak met behulp van printf, printfnen sprintfgerelateerde functies. Bijvoorbeeld:

dotnet fsi

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

geeft de uitvoer

Hello world, 2 + 2 is 4

Met F# kunnen gestructureerde waarden ook worden opgemaakt als tekst zonder opmaak. Bekijk bijvoorbeeld het volgende voorbeeld waarmee de uitvoer wordt opgemaakt als een matrixachtige weergave van tuples.

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

Gestructureerde tekstopmaak zonder opmaak wordt geactiveerd wanneer u de %A opmaak in printf opmaaktekenreeksen gebruikt. Het wordt ook geactiveerd bij het opmaken van de uitvoer van waarden in F# interactief, waarbij de uitvoer extra informatie bevat en ook kan worden aangepast. Opmaak van tekst zonder opmaak is ook waarneembaar via aanroepen van x.ToString() F#-samenvoegings- en recordwaarden, inclusief waarden die impliciet voorkomen in foutopsporing, logboekregistratie en andere hulpprogramma's.

Controleren van printftekenreeksen opmaken

Er wordt een compilatiefout gerapporteerd als een printf opmaakfunctie wordt gebruikt met een argument dat niet overeenkomt met de opmaakaanduidingen van de afdrukf in de notatietekenreeks. Bijvoorbeeld:

sprintf "Hello %s" (2+2)

geeft de uitvoer

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

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

Technisch gesproken controleert een speciale regel in de F#-compiler bij het gebruik van printf en andere gerelateerde functies de letterlijke tekenreeks die wordt doorgegeven als de notatietekenreeks, zodat de volgende argumenten van het juiste type overeenkomen met de gebruikte notatieaanduidingen.

Opmaakaanduidingen voor printf

Indelingsspecificaties voor printf notaties zijn tekenreeksen met % markeringen die de opmaak aangeven. Tijdelijke aanduidingen opmaken, bestaat uit %[flags][width][.precision][type] de manier waarop het type als volgt wordt geïnterpreteerd:

Opmaakaanduiding Type(n) Opmerkingen
%b bool (System.Boolean) Opgemaakt als true of false
%s string (System.String) Opgemaakt als niet-gescaped inhoud
%c char (System.Char) Opgemaakt als letterlijk teken
%d, %i een eenvoudig geheel getaltype Opgemaakt als een decimaal geheel getal, ondertekend als het basistype geheel getal is ondertekend
%u een eenvoudig geheel getaltype Opgemaakt als een niet-ondertekend decimaal geheel getal
%x, %X een eenvoudig geheel getaltype Opgemaakt als een niet-ondertekend hexadecimaal getal (respectievelijk a-f of A-F voor hexe cijfers)
%o een eenvoudig geheel getaltype Opgemaakt als een niet-ondertekend octaal getal
%B een eenvoudig geheel getaltype Opgemaakt als een niet-ondertekend binair getal
%e, %E een basistype voor drijvende komma Opgemaakt als een ondertekende waarde met het formulier [-]d.dddde[sign]ddd waarbij d één decimaalteken is, is dddd een of meer decimale cijfers, ddd precies drie decimale cijfers en teken is + of -
%f, %F een basistype voor drijvende komma Opgemaakt als een ondertekende waarde met het formulier [-]dddd.dddd, waarbij dddd een of meer decimale cijfers zijn. Het aantal cijfers vóór het decimaalteken is afhankelijk van de grootte van het getal en het aantal cijfers na het decimaalteken is afhankelijk van de aangevraagde precisie.
%g, %G een basistype voor drijvende komma Opgemaakt met behulp van een ondertekende waarde die wordt afgedrukt in %f of %e opgemaakt, afhankelijk van wat compacter is voor de opgegeven waarde en precisie.
%M a decimal (System.Decimal) waarde Opgemaakt met de "G" notatieaanduiding voor System.Decimal.ToString(format)
%O elke waarde Opgemaakt door het object te boksen en de methode aan System.Object.ToString() te roepen
%A elke waarde Opgemaakt met gestructureerde tekstopmaak zonder opmaak met de standaardindelingsinstellingen
%a elke waarde Hiervoor zijn twee argumenten vereist: een opmaakfunctie die een contextparameter en de waarde accepteert, en de specifieke waarde die moet worden afgedrukt
%t elke waarde Vereist één argument: een opmaakfunctie die een contextparameter accepteert die de juiste tekst uitvoert of retourneert
%% (geen) Hiervoor zijn geen argumenten vereist en wordt een gewoon procentteken afgedrukt: %

Typen basis gehele getallen zijn byte (), (System.Byte), sbyte (System.SByte), int16uint16 (System.Int16), (System.UInt16), int32 (System.Int32), uint32 (System.UInt32), (), int64 (System.Int64), uint64 (System.UInt64System.IntPtr) nativeint en unativeint (System.UIntPtr). Basistypen voor drijvende komma zijn float (), float32 (System.DoubleSystem.Single) en decimal (System.Decimal).

De optionele breedte is een geheel getal dat de minimale breedte van het resultaat aangeeft. Hiermee drukt u bijvoorbeeld %6d een geheel getal af, waarbij het wordt voorafgegaan door spaties om ten minste zes tekens in te vullen. Als de breedte is *, wordt een extra geheel getal gebruikt om de bijbehorende breedte op te geven.

Geldige vlaggen zijn:

Vlag Effect
0 Nullen toevoegen in plaats van spaties om de vereiste breedte te vormen
- Links het resultaat binnen de opgegeven breedte uitvullen
+ Voeg een + teken toe als het getal positief is (om een - teken voor negatieven te vinden)
spatieteken Voeg een extra spatie toe als het getal positief is (om overeen te komen met een '-' teken voor negatieven)

De vlag printf # is ongeldig en er wordt een compilatiefout gerapporteerd als deze wordt gebruikt.

Waarden worden opgemaakt met behulp van invariante cultuur. Cultuurinstellingen zijn niet relevant voor printf opmaak, behalve wanneer deze van invloed zijn op de resultaten en %O%A opmaak. Zie gestructureerde tekstopmaak zonder opmaak voor meer informatie.

%A Opmaak

De %A indelingsaanduiding wordt gebruikt om waarden op een door mensen leesbare manier op te maken en kan ook handig zijn voor het rapporteren van diagnostische gegevens.

Primitieve waarden

Wanneer u tekst zonder opmaak opmaakt met de %A aanduiding, worden F#-numerieke waarden opgemaakt met het achtervoegsel en de invariante cultuur. Drijvende-kommawaarden worden opgemaakt met behulp van 10 plaatsen met precisie van drijvende komma. Bijvoorbeeld:

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

Produceert

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

Wanneer u de %A aanduiding gebruikt, worden tekenreeksen opgemaakt met aanhalingstekens. Escapecodes worden niet toegevoegd en in plaats daarvan worden de onbewerkte tekens afgedrukt. Bijvoorbeeld:

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

Produceert

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

.NET-waarden

Wanneer u tekst zonder opmaak opmaakt met behulp van de %A aanduiding, worden niet-F# .NET-objecten opgemaakt met behulp van x.ToString() de standaardinstellingen van .NET gegeven door System.Globalization.CultureInfo.CurrentCulture en System.Globalization.CultureInfo.CurrentUICulture. Bijvoorbeeld:

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

Produceert

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

Gestructureerde waarden

Wanneer u tekst zonder opmaak opmaakt met de %A aanduiding, wordt blok inspringing gebruikt voor F#-lijsten en tuples. Dit wordt weergegeven in het vorige voorbeeld. De structuur van matrices wordt ook gebruikt, waaronder multidimensionale matrices. Eendimensionale matrices worden weergegeven met [| ... |] syntaxis. Bijvoorbeeld:

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

Produceert

[|(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)|]

De standaard afdrukbreedte is 80. Deze breedte kan worden aangepast met behulp van een afdrukbreedte in de opmaakaanduiding. Bijvoorbeeld:

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

Produceert

[|(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)|]

Als u een afdrukbreedte van 0 opgeeft, wordt er geen afdrukbreedte gebruikt. Eén regel tekst resulteert, behalve wanneer ingesloten tekenreeksen in de uitvoer regeleinden bevatten. Bijvoorbeeld

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

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

Produceert

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

Een dieptelimiet van 4 wordt gebruikt voor reekswaarden (IEnumerable) die worden weergegeven als seq { ...}. Er wordt een dieptelimiet van 100 gebruikt voor lijst- en matrixwaarden. Bijvoorbeeld:

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

Produceert

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

Blok inspringing wordt ook gebruikt voor de structuur van openbare records en samenvoegwaarden. Bijvoorbeeld:

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

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

Produceert

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

Als %+A deze wordt gebruikt, wordt de privéstructuur van records en samenvoegingen ook onthuld met behulp van reflectie. Bijvoorbeeld

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

Produceert

external view:
R

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

Grote, cyclische of diep geneste waarden

Grote gestructureerde waarden worden opgemaakt tot een maximaal totaal aantal objectknooppunten van 10000. Diep geneste waarden worden opgemaakt tot een diepte van 100. In beide gevallen ... wordt gebruikt om een deel van de uitvoer te verwijderen. Bijvoorbeeld:

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)

produceert een grote uitvoer met enkele onderdelen:

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

Cycli worden gedetecteerd in de objectgrafieken en ... worden gebruikt op plaatsen waar cycli worden gedetecteerd. Bijvoorbeeld

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

Produceert

{ Links = [...] }

Luie, null- en functiewaarden

Luie waarden worden afgedrukt als Value is not created of gelijkwaardige tekst wanneer de waarde nog niet is geëvalueerd.

Null-waarden worden afgedrukt als null het statische type van de waarde wordt bepaald als een samenvoegingstype waarbij null een toegestane weergave is.

F#-functiewaarden worden bijvoorbeeld afgedrukt als de intern gegenereerde sluitingsnaam <fun:it@43-7>.

Opmaak van tekst zonder opmaak aanpassen met StructuredFormatDisplay

Wanneer u de %A aanduiding gebruikt, wordt de aanwezigheid van het StructuredFormatDisplay kenmerk voor typedeclaraties gerespecteerd. Dit kan worden gebruikt om surrogaattekst en -eigenschap op te geven om een waarde weer te geven. Voorbeeld:

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

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

Produceert

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

Opmaak van tekst zonder opmaak aanpassen door te overschrijven ToString

De standaard implementatie van ToString is waarneembaar in F#-programmering. Vaak zijn de standaardresultaten niet geschikt voor gebruik in de programmeurgerichte informatieweergave of gebruikersuitvoer. Als gevolg hiervan is het gebruikelijk om de standaard implementatie te overschrijven.

Standaard overschrijven F#-record- en samenvoegtypen de implementatie met ToString een implementatie die wordt gebruikt sprintf "%+A". Bijvoorbeeld:

type Counts = { Clicks:int list }

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

Produceert

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

Voor klassetypen is er geen standaard implementatie ToString van opgegeven en wordt de standaard .NET-standaard gebruikt, waarmee de naam van het type wordt gerapporteerd. Bijvoorbeeld:

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

Produceert

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

Het toevoegen van een onderdrukking voor ToString kan een betere opmaak geven.

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

Produceert

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

Opmaak van tekst zonder opmaak aanpassen met StructuredFormatDisplay en ToString

Als u consistente opmaak voor %A en %O opmaakaanduidingen wilt bereiken, combineert u het gebruik van StructuredFormatDisplay met een onderdrukking van ToString. Bijvoorbeeld:

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

    override _.ToString() = "Custom ToString"

De volgende definities evalueren

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

geeft de 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]"

Het gebruik van StructuredFormatDisplay met de ondersteunende DisplayText eigenschap betekent dat het feit dat het myRec een structureel recordtype is genegeerd tijdens gestructureerd afdrukken en dat de onderdrukking in ToString() alle omstandigheden de voorkeur heeft.

Een implementatie van de System.IFormattable interface kan worden toegevoegd voor verdere aanpassing in aanwezigheid van specificaties voor .NET-indeling.

F# Interactief gestructureerd afdrukken

F# Interactive (dotnet fsi) maakt gebruik van een uitgebreide versie van gestructureerde tekstopmaak zonder opmaak om waarden te rapporteren en biedt aanvullende aanpassingsmogelijkheden. Zie F# Interactive voor meer informatie.

Weergaven voor foutopsporing aanpassen

Foutopsporingsprogramma's voor .NET respecteren het gebruik van kenmerken zoals DebuggerDisplay en DebuggerTypeProxy, en deze hebben invloed op de gestructureerde weergave van objecten in foutopsporingsprogramma-inspectievensters. De F#-compiler heeft deze kenmerken automatisch gegenereerd voor gediscrimineerde samenvoeg- en recordtypen, maar niet voor klasse-, interface- of structtypen.

Deze kenmerken worden genegeerd in F#-opmaak voor tekst zonder opmaak, maar het kan handig zijn om deze methoden te implementeren om weergaven te verbeteren bij het opsporen van fouten in F#-typen.

Zie ook