Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O F# dá suporte à formatação marcada por tipo de texto sem formatação usando printf
, printfn
e sprintf
funções relacionadas.
Por exemplo
dotnet fsi
> printfn "Hello %s, %d + %d is %d" "world" 2 2 (2+2);;
fornece a saída
Hello world, 2 + 2 is 4
O F# também permite que valores estruturados sejam formatados como texto sem formatação. Por exemplo, considere o exemplo a seguir que formata a saída como uma exibição de tuplas semelhante à matriz.
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)]]
A formatação de texto sem formatação estruturada é ativada quando você usa o %A
formato em printf
cadeias de caracteres de formatação.
Ele também é ativado ao formatar a saída de valores em F# interativo, onde a saída inclui informações extras e é personalizável adicionalmente.
A formatação de texto sem formatação também é observável por meio de chamadas para x.ToString()
valores de registro e união F#, incluindo aqueles que ocorrem implicitamente na depuração, registro em log e outras ferramentas.
Verificação de cadeias de printf
caracteres de formato
Um erro de tempo de compilação será relatado se uma printf
função de formatação for usada com um argumento que não corresponda aos especificadores de formato printf na cadeia de caracteres de formato. Por exemplo
sprintf "Hello %s" (2+2)
fornece a saída
sprintf "Hello %s" (2+2)
----------------------^
stdin(3,25): error FS0001: The type 'string' does not match the type 'int'
Tecnicamente falando, ao usar printf
e outras funções relacionadas, uma regra especial no compilador F# verifica o literal da cadeia de caracteres passado como a cadeia de caracteres de formato, garantindo que os argumentos subsequentes aplicados sejam do tipo correto para corresponder aos especificadores de formato usados.
Especificadores de formato para printf
As especificações de formato para printf
formatos são cadeias de caracteres com %
marcadores que indicam o formato. Os espaços reservados de formato consistem em %[flags][width][.precision][type]
onde o tipo é interpretado da seguinte maneira:
Especificador de formato | Type(s) | Observações |
---|---|---|
%b |
bool (System.Boolean ) |
Formatado como true ou false |
%s |
string (System.String ) |
Formatado como seu conteúdo sem escape |
%c |
char (System.Char ) |
Formatado como o literal de caractere |
%d , %i |
um tipo inteiro básico | Formatado como um inteiro decimal, assinado se o tipo inteiro básico estiver assinado |
%u |
um tipo inteiro básico | Formatado como um inteiro decimal sem sinal |
%x , %X |
um tipo inteiro básico | Formatado como um número hexadecimal não assinado (a-f ou A-F para dígitos hex, respectivamente) |
%o |
um tipo inteiro básico | Formatado como um número octal sem sinal |
%B |
um tipo inteiro básico | Formatado como um número binário sem sinal |
%e , %E |
um tipo de ponto flutuante básico | Formatado como um valor assinado com o formulário [-]d.dddde[sign]ddd em que d é um único dígito decimal, dddd é um ou mais dígitos decimais, ddd é exatamente três dígitos decimais e o sinal é + ou - |
%f , %F |
um tipo de ponto flutuante básico | Formatado como um valor assinado com o formulário [-]dddd.dddd , onde dddd está um ou mais dígitos decimais. O número de dígitos antes do ponto decimal depende da magnitude do número e o número de dígitos após o ponto decimal depende da precisão solicitada. |
%g , %G |
um tipo de ponto flutuante básico | Formatado usando como um valor assinado impresso em %f ou %e formato, o que for mais compacto para o valor e a precisão fornecidos. |
%M |
a decimal (System.Decimal ) value |
Formatado usando o "G" especificador de formato para System.Decimal.ToString(format) |
%O |
qualquer valor | Formatado por boxing do objeto e chamando seu System.Object.ToString() método |
%A |
qualquer valor | Formatado usando a formatação de texto sem formatação estruturada com as configurações de layout padrão |
%a |
qualquer valor | Requer dois argumentos: uma função de formatação aceitando um parâmetro de contexto e o valor e o valor específico para imprimir |
%t |
qualquer valor | Requer um argumento: uma função de formatação aceitando um parâmetro de contexto que gera ou retorna o texto apropriado |
%% |
(nenhuma) | Não requer argumentos e imprime um sinal de porcentagem simples: % |
Os tipos inteiros básicos são (), (), int16
(System.SByte
System.Int16
), uint16
(System.UInt16
), int32
(), (System.Int32
), uint32
(System.UInt32
), int64
(System.Int64
), uint64
(System.UInt64
), (System.IntPtr
) nativeint
e unativeint
(System.UIntPtr
). sbyte
System.Byte
byte
Tipos básicos de ponto flutuante são float
(System.Double
), float32
(System.Single
) e decimal
(System.Decimal
).
A largura opcional é um inteiro que indica a largura mínima do resultado. Por exemplo, %6d
imprime um inteiro, prefixando-o com espaços para preencher pelo menos seis caracteres. Se a largura for *
, um argumento inteiro extra será usado para especificar a largura correspondente.
Os sinalizadores válidos são:
Bandeira | Efeito |
---|---|
0 |
Adicionar zeros em vez de espaços para compor a largura necessária |
- |
A esquerda justifica o resultado dentro da largura especificada |
+ |
Adicione um + caractere se o número for positivo (para corresponder a um - sinal de negativo) |
caractere de espaço | Adicione um espaço extra se o número for positivo (para corresponder a um sinal '-' para negativos) |
O sinalizador printf #
é inválido e um erro de tempo de compilação será relatado se ele for usado.
Os valores são formatados usando a cultura invariável. As configurações de cultura são irrelevantes para printf
formatação, exceto quando afetam os resultados e %O
%A
a formatação. Para obter mais informações, consulte a formatação de texto sem formatação estruturada.
%A
formatação
O %A
especificador de formato é usado para formatar valores de maneira legível por humanos e também pode ser útil para relatar informações de diagnóstico.
Valores primitivos
Ao formatar texto sem formatação usando o %A
especificador, os valores numéricos F# são formatados com o sufixo e a cultura invariável. Os valores de ponto flutuante são formatados usando 10 locais de precisão de ponto flutuante. Por exemplo
printfn "%A" (1L, 3n, 5u, 7, 4.03f, 5.000000001, 5.0000000001)
Produz
(1L, 3n, 5u, 7, 4.03000021f, 5.000000001, 5.0)
Ao usar o especificador, as %A
cadeias de caracteres são formatadas usando aspas. Códigos de escape não são adicionados e, em vez disso, os caracteres brutos são impressos. Por exemplo
printfn "%A" ("abc", "a\tb\nc\"d")
Produz
("abc", "a b
c"d")
Valores do .NET
Ao formatar texto sem formatação usando o %A
especificador, os objetos .NET não F# são formatados usando x.ToString()
as configurações padrão do .NET fornecidas por System.Globalization.CultureInfo.CurrentCulture
e System.Globalization.CultureInfo.CurrentUICulture
. Por exemplo
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
Produz
Culture 1: 31.12.1999 00:00:00
Culture 2: 12/31/1999 12:00:00 AM
Valores estruturados
Ao formatar texto sem formatação usando o especificador, o %A
recuo de bloco é usado para listas e tuplas F#. Isso é mostrado no exemplo anterior.
A estrutura de matrizes também é usada, incluindo matrizes multidimensionais. Matrizes unidimensionais são mostradas com [| ... |]
sintaxe. Por exemplo
printfn "%A" [| for i in 1 .. 20 -> (i, i*i) |]
Produz
[|(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)|]
A largura de impressão padrão é 80. Essa largura pode ser personalizada usando uma largura de impressão no especificador de formato. Por exemplo
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) |]
Produz
[|(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)|]
Especificar uma largura de impressão de 0 resulta em nenhuma largura de impressão sendo usada. Uma única linha de texto resultará, exceto quando as cadeias de caracteres inseridas na saída contiverem quebras de linha. Por exemplo
printfn "%0A" [| for i in 1 .. 5 -> (i, i*i) |]
printfn "%0A" [| for i in 1 .. 5 -> "abc\ndef" |]
Produz
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]
[|"abc
def"; "abc
def"; "abc
def"; "abc
def"; "abc
def"|]
Um limite de profundidade de 4 é usado para valores de sequência (IEnumerable
), que são mostrados como seq { ...}
. Um limite de profundidade de 100 é usado para valores de lista e matriz.
Por exemplo
printfn "%A" (seq { for i in 1 .. 10 -> (i, i*i) })
Produz
seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]
O recuo de bloco também é usado para a estrutura de valores de registro público e união. Por exemplo
type R = { X : int list; Y : string list }
printfn "%A" { X = [ 1;2;3 ]; Y = ["one"; "two"; "three"] }
Produz
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
Se %+A
for usado, a estrutura privada de registros e uniões também será revelada usando reflexão. Por exemplo
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
Produz
external view:
R
internal view:
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
Valores grandes, cíclicos ou profundamente aninhados
Valores estruturados grandes são formatados para uma contagem máxima de nó de objeto geral de 10000.
Valores profundamente aninhados são formatados para uma profundidade de 100. Em ambos os casos ...
, é usado para reexibir parte da saída. Por exemplo
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)
produz uma saída grande com algumas partes excluídas:
Node(Tip, Node(Tip, ....Node (..., ...)...))
Os ciclos são detectados nos grafos de objeto e ...
são usados em locais onde os ciclos são detectados. Por exemplo
type R = { mutable Links: R list }
let r = { Links = [] }
r.Links <- [r]
printfn "%A" r
Produz
{ Links = [...] }
Valores lentos, nulos e de função
Valores lentos são impressos como Value is not created
ou texto equivalente quando o valor ainda não foi avaliado.
Os valores nulos são impressos como null
, a menos que o tipo estático do valor seja determinado como um tipo de união em null
que é uma representação permitida.
Os valores da função F# são impressos como o nome de fechamento gerado internamente, por exemplo, <fun:it@43-7>
.
Personalizar a formatação de texto sem formatação com StructuredFormatDisplay
Ao usar o %A
especificador, a presença do StructuredFormatDisplay
atributo em declarações de tipo é respeitada. Isso pode ser usado para especificar texto alternativo e propriedade para exibir um valor. Por exemplo:
[<StructuredFormatDisplay("Counts({Clicks})")>]
type Counts = { Clicks:int list}
printfn "%20A" {Clicks=[0..20]}
Produz
Counts([0; 1; 2; 3;
4; 5; 6; 7;
8; 9; 10; 11;
12; 13; 14;
15; 16; 17;
18; 19; 20])
Personalizar a formatação de texto sem formatação substituindo ToString
A implementação padrão é ToString
observável na programação F#. Geralmente, os resultados padrão não são adequados para uso na exibição de informações voltadas para o programador ou na saída do usuário e, como resultado, é comum substituir a implementação padrão.
Por padrão, os tipos de registro F# e união substituem a implementação com ToString
uma implementação que usa sprintf "%+A"
. Por exemplo
type Counts = { Clicks:int list }
printfn "%s" ({Clicks=[0..10]}.ToString())
Produz
{ Clicks = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10] }
Para tipos de ToString
classe, nenhuma implementação padrão é fornecida e o padrão .NET é usado, que relata o nome do tipo. Por exemplo
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())
Produz
Default structured print gives this:
[MyClassType; MyClassType]
Default ToString gives:
[MyClassType; MyClassType]
Adicionar uma substituição pode ToString
fornecer melhor formatação.
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())
Produz
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])]
Personalizar a formatação de texto sem formatação com StructuredFormatDisplay
e ToString
Para obter formatação consistente para %A
e %O
formatar especificadores, combine o uso com StructuredFormatDisplay
uma substituição de ToString
. Por exemplo
[<StructuredFormatDisplay("{DisplayText}")>]
type MyRecord =
{
a: int
}
member this.DisplayText = this.ToString()
override _.ToString() = "Custom ToString"
Avaliando as definições a seguir
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]}"
fornece o texto
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]"
O uso com StructuredFormatDisplay
a propriedade de suporte DisplayText
significa que o myRec
tipo de registro estrutural é ignorado durante a impressão estruturada e a substituição ToString()
é preferencial em todas as circunstâncias.
Uma implementação da System.IFormattable
interface pode ser adicionada para personalização adicional na presença de especificações de formato .NET.
Impressão estruturada interativa F#
O F# Interativo (dotnet fsi
) usa uma versão estendida da formatação de texto sem formatação estruturada para relatar valores e permite personalização adicional. Para obter mais informações, consulte F# Interativo.
Personalizar exibições de depuração
Os depuradores do .NET respeitam o uso de atributos como DebuggerDisplay
e DebuggerTypeProxy
, portanto, afetam a exibição estruturada de objetos nas janelas de inspeção do depurador.
O compilador F# gerou automaticamente esses atributos para tipos de registro e união discriminados, mas não tipos de classe, interface ou struct.
Esses atributos são ignorados na formatação de texto sem formatação F#, mas pode ser útil implementar esses métodos para melhorar as exibições ao depurar tipos F#.