Formato de texto sin formato

F# admite el formato comprobado por tipos de texto sin formato mediante printf , , y funciones printfn sprintf relacionadas. Por ejemplo,

dotnet fsi

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

proporciona la salida

Hello world, 2 + 2 is 4

F# también permite dar formato a los valores estructurados como texto sin formato. Por ejemplo, considere el ejemplo siguiente que da formato a la salida como una presentación de tipo matriz de tuplas.

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

El formato de texto sin formato estructurado se activa cuando se usa %A el formato en printf cadenas de formato. También se activa al dar formato a la salida de valores en F# interactivo, donde la salida incluye información adicional y es personalizable. El formato de texto sin formato también se puede observar a través de las llamadas a en valores de unión y registro de F#, incluidos los que se producen implícitamente en la depuración, el registro y otras x.ToString() herramientas.

Comprobación de printf cadenas de formato

Se notifica un error en tiempo de compilación si se usa una función de formato con un argumento que no coincide con los especificadores de formato printf printf de la cadena de formato. Por ejemplo,

sprintf "Hello %s" (2+2)

proporciona la salida

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

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

Técnicamente, al usar y otras funciones relacionadas, una regla especial del compilador de F# comprueba el literal de cadena pasado como cadena de formato, lo que garantiza que los argumentos subsiguientes aplicados sean del tipo correcto para que coincidan con los especificadores de formato printf utilizados.

Especificadores de formato para printf

Las especificaciones de printf formato para los formatos son % cadenas con marcadores que indican formato. Los marcadores de posición de formato %[flags][width][.precision][type] constan de donde el tipo se interpreta de la manera siguiente:

Especificador de formato Tipos Comentarios
%b bool (System.Boolean) Con formato true o false
%s string (System.String) Con formato como su contenido sin desencapsular
%c char (System.Char) Con formato como literal de carácter
%d, %i un tipo entero básico Con formato de entero decimal, con signo si el tipo entero básico tiene signo
%u un tipo entero básico Con formato de entero decimal sin signo
%x, %X un tipo entero básico Con formato de número hexadecimal sin signo (a-f o A-F para dígitos hexadecimales respectivamente)
%o un tipo entero básico Con formato de número octal sin signo
%B un tipo entero básico Con formato de número binario sin signo
%e, %E un tipo de punto flotante básico Con formato de valor con signo con el formato donde d es un dígito decimal único, dddd es uno o varios dígitos [-]d.dddde[sign]ddd decimales, ddd es exactamente tres dígitos decimales y sign es o +``-
%f, %F un tipo de punto flotante básico Con formato como un valor con signo con el formato [-]dddd.dddd , donde es uno o varios dddd dígitos decimales. El número de dígitos que hay delante del separador decimal depende de la magnitud del número y el número de dígitos que hay detrás del separador decimal depende de la precisión solicitada.
%g, %G un tipo de punto flotante básico Con formato usando como un valor con firma impreso en formato o , lo que sea más compacto para el valor y la %f %e precisión especificados.
%M a decimal ( System.Decimal ) value Con formato mediante el "G" especificador de formato para System.Decimal.ToString(format)
%O cualquier valor Con formato al aplicar una caja al objeto y llamar a su System.Object.ToString() método
%A cualquier valor Con formato con formato de texto sin formato estructurado con la configuración de diseño predeterminada
%a cualquier valor Requiere dos argumentos: una función de formato que acepta un parámetro de contexto y el valor, y el valor concreto que se debe imprimir.
%t cualquier valor Requiere un argumento: una función de formato que acepta un parámetro de contexto que genera o devuelve el texto adecuado.
%% (ninguno) No requiere argumentos e imprime un signo de porcentaje sin formato: %

Los tipos enteros básicos byte son ( ), ( ), ( ), ( System.Byte ), sbyte ( ), ( System.SByte ), int16 ( ), ( ), System.Int16 ( ) y uint16 System.UInt16 int32 ( System.Int32 uint32 System.UInt32 int64 System.Int64 uint64 System.UInt64 nativeint System.IntPtr unativeint System.UIntPtr ). Los tipos de punto flotante básicos float son ( ), ( ) y ( System.Double float32 System.Single decimal System.Decimal ).

El ancho opcional es un entero que indica el ancho mínimo del resultado. Por ejemplo, %6d imprime un entero y lo prefida con espacios para rellenar al menos seis caracteres. Si width es * , se toma un argumento entero adicional para especificar el ancho correspondiente.

Las marcas válidas son:

Marca Efecto Comentarios
0 Agregue ceros en lugar de espacios para crear el ancho necesario.
- La izquierda justifica el resultado dentro del ancho especificado
+ Agregue un + carácter si el número es positivo (para que coincida con un signo de - negativos).
carácter de espacio Agregue un espacio adicional si el número es positivo (para que coincida con un signo "-" para los negativos)

La marca printf no es válida y se notifica un error en tiempo de # compilación si se usa.

Los valores se formatearán mediante la referencia cultural invariable. La configuración de referencia cultural es printf irrelevante para el formato, excepto cuando afectan a los resultados de %O y el %A formato. Para obtener más información, vea formato de texto sin formato estructurado.

%A Formato

El especificador de formato se usa para dar formato a los valores de una manera legible y también puede ser útil para notificar %A información de diagnóstico.

Valores primitivos

Al dar formato al texto sin formato mediante el especificador, los valores numéricos de F# tienen formato con su sufijo y %A su referencia cultural invariable. Los valores de punto flotante tienen formato con 10 lugares de precisión de punto flotante. Por ejemplo,

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

Produce

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

Cuando se usa %A el especificador, las cadenas tienen formato mediante comillas. No se agregan códigos de escape y, en su lugar, se imprimen los caracteres sin formato. Por ejemplo,

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

Produce

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

Valores de .NET

Al dar formato al texto sin formato mediante el especificador, se da formato a los objetos .NET que no son de F# mediante el uso de la configuración predeterminada de %A x.ToString() .NET especificada por System.Globalization.CultureInfo.CurrentCulture y System.Globalization.CultureInfo.CurrentUICulture . Por ejemplo,

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

Produce

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

Valores estructurados

Al aplicar formato al texto sin formato mediante el especificador, se usa la sangría de bloques %A para las listas de F# y las tuplas. Esto se muestra en el ejemplo anterior. También se usa la estructura de matrices, incluidas las matrices multidimensionales. Las matrices unidimensionales se muestran con [| ... |] sintaxis. Por ejemplo,

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

Produce

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

El ancho de impresión predeterminado es 80. Este ancho se puede personalizar mediante un ancho de impresión en el especificador de formato. Por ejemplo,

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

Produce

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

Si se especifica un ancho de impresión de 0, no se usará ningún ancho de impresión. Se dará como resultado una sola línea de texto, excepto cuando las cadenas incrustadas en la salida contienen saltos de línea. Por ejemplo

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

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

Produce

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

Se usa un límite de profundidad de 4 para los valores de secuencia ( IEnumerable ), que se muestran como seq { ...} . Se usa un límite de profundidad de 100 para los valores de lista y matriz. Por ejemplo,

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

Produce

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

La sangría de bloques también se usa para la estructura de los valores de registro y unión públicos. Por ejemplo,

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

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

Produce

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

Si se usa , la estructura privada de registros y uniones también se revela %+A mediante la reflexión. Por ejemplo

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

Produce

external view:
R

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

Valores grandes, cíclicos o profundamente anidados

Los valores estructurados grandes tienen un formato de 10 000 nodos de objeto total máximo. Los valores profundamente anidados tienen un formato de profundidad de 100. En ambos ... casos se usa para evitar parte de la salida. Por ejemplo,

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)

genera una salida grande con algunas partes elided:

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

Los ciclos se detectan en los gráficos de objetos y ... se usan en lugares donde se detectan ciclos. Por ejemplo

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

Produce

{ Links = [...] }

Valores diferido, nulo y de función

Los valores diferido se imprimen como Value is not created texto equivalente o cuando el valor aún no se ha evaluado.

Los valores NULL se imprimen como a menos que se determine que el tipo estático del valor es un tipo null de unión donde es una representación null permitida.

Los valores de función de F# se imprimen como su nombre de cierre generado internamente, por ejemplo, <fun:it@43-7> .

Personalización del formato de texto sin formato con StructuredFormatDisplay

Cuando se usa el especificador, se respeta la presencia del atributo en las %A StructuredFormatDisplay declaraciones de tipo. Se puede usar para especificar el texto suplente y la propiedad para mostrar un valor. Por ejemplo:

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

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

Produce

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

Personalización del formato de texto sin formato mediante la invalidación ToString

La implementación predeterminada de ToString es observable en la programación de F#. A menudo, los resultados predeterminados no son adecuados para su uso en la presentación de información orientada al programador o en la salida del usuario y, como resultado, es habitual invalidar la implementación predeterminada.

De forma predeterminada, los tipos de registro y unión de F# invalidan la implementación de ToString con una implementación que usa sprintf "%+A" . Por ejemplo,

type Counts = { Clicks:int list }

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

Produce

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

Para los tipos de clase, no se proporciona ninguna implementación predeterminada de y se usa el valor predeterminado de .NET, que informa ToString del nombre del tipo. Por ejemplo,

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

Produce

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

Agregar una invalidación para ToString puede proporcionar un mejor formato.

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

Produce

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

F# interactivo impresión estructurada

F# interactivo ( ) usa una versión extendida de formato de texto sin formato estructurado para notificar valores dotnet fsi y permite una personalización adicional. Para obtener más información, vea F# interactivo.

Personalización de las pantallas de depuración

Los depuradores de .NET respetan el uso de atributos como y , y afectan a la presentación estructurada de objetos en DebuggerDisplay las ventanas de inspección del DebuggerTypeProxy depurador. El compilador de F# genera automáticamente estos atributos para tipos de unión y registro discriminados, pero no tipos de clase, interfaz o estructura.

Estos atributos se omiten en el formato de texto sin formato de F#, pero puede ser útil implementar estos métodos para mejorar las pantallas al depurar tipos de F#.

Vea también