1
votes

Comment envelopper printfn en F #?

J'ai lu sur les suites et les applications partielles; Je connais également la fonction kprintf.

Mais je ne sais toujours pas comment écrire quelque chose comme:

myPrintFunction "%s : %i" "hello" 3

quelle serait la syntaxe ici? p>

pour que je puisse l'utiliser comme:

let myPrintFunction format variable_length_arguments_list =
    let a = sprintf format variable_length_ argument_list
    do other things

Modifier:

Ceci est différent de Comment implémenter une méthode avec un nombre variable d'arguments? a> parce que cette question demande comment créer une méthode avec un nombre variable d'arguments, mais le problème auquel je suis confronté est de passer ce nombre variable d'arguments à la fonction suivante (sprintf) qui prend également un nombre variable d'arguments. Ou du moins c'est là que je suppose que le problème se trouve.

Le code de test, basé sur la solution proposée par Scott peut être trouvé ici: https://dotnetfiddle.net/oCzcS9

f#

2 commentaires

Est-ce que cela répond à votre question? Comment implémenter une méthode avec un nombre variable d'arguments?


Non, le système au format F # n'existe pas en C #.


3 Réponses :


0
votes
open System

let myPrintFunction (format: Printf.StringFormat<_>) ([<ParamArray>] args) =
    let a = sprintf format args
    a

myPrintFunction "%s : %i" "hello" 3

9 commentaires

Existe-t-il un moyen de connaître le nombre d'arguments dans «args»?


en fait, elle ne fonctionne pas lorsqu'elle est appelée depuis une classe: [FS0003] Cette valeur n'est pas une fonction et ne peut pas être appliquée. Le code étant: let Print (format: Printf.StringFormat <_>) ([] args) = \ n let message = sprintf format args \ n ConsoleInfoEvent.Trigger message \ n printfn "% s" message


et l'appel étant: Imprimer "% s:% i" "bonjour" 3


Vous voulez donc que myPrintFunction soit une fonction membre d'une classe?


Oui; Je ne comprends pas pourquoi le code serait différent?


Ce lien violon ne semblait pas arriver au bon code.


Assurez-vous que votre fonction principale renvoie un int comme 0. Cette réponse fonctionne telle quelle, mais pas si vous essayez d'ajouter un effet secondaire comme un appel printfn .


ce que cette fonction est censée faire, c'est prendre des paramètres, comme un printfn, générer une chaîne avec sprintf, appeler un événement et afficher le résultat. J'ai vu du code similaire en ligne avec kprintf, mais je n'ai pas très bien compris comment fonctionne kprintf


Peut-être que cela vous aidera à mieux comprendre kprintf: stackoverflow.com/a/5570101/5652483



0
votes

Pour ajouter PrintF en tant que fonction membre, c'est la plus proche que je puisse obtenir. Comme vous le voyez, j'ai dû passer la chaîne de format séparément (dans le constructeur, ou j'aurais pu utiliser un setter de propriété). Je n'ai trouvé aucun moyen de passer la chaîne de format comme premier paramètre de la fonction PrintF comme je le pouvais pour une fonction gratuite (voir mon autre réponse à https://stackoverflow.com/a/58822618/5652483 ).

De plus, si je décommente la ligne this.RaiseSomeEvent msg , alors ça casse. Je n'ai donc trouvé aucun moyen d'activer la fonction PrintF pour avoir un effet secondaire.

J'espère que quelqu'un d'autre pourra résoudre ces problèmes.

type Foo (format: Printf.StringFormat<_>) =
    member this.RaiseSomeEvent msg = printf "%s" msg

    member this.PrintF ([<ParamArray>] args) =
        let msg = sprintf format args
        //this.RaiseSomeEvent msg
        msg

let foo = Foo("%s : %i")
foo.PrintF "hello" 3


0 commentaires

2
votes

Je veux démontrer la fonction ksprintf, car celle-ci accepte une suite qui vous permettra de passer la chaîne résultante à par exemple un système de journalisation.

À des fins de démonstration, créons d'abord quelque chose qui peut prendre une seule chaîne comme entrée et le transmettre, dans ce cas à la console.

        }

Alors maintenant, si writeStringToConsole est tout ce que nous avons, comment le faire accepter le formatage F #?

        member _.Debugf format = dummy format
        member _.Verbosef format = dummy format
        member _.Infof format = dummy format
        member _.Warningf format = dummy format
        member _.Errorf format = dummy format
        member _.Fatalf format = dummy format

Exemple qui démontre que cela fonctionne.

let slogf = Printf.ksprintf

let dummyLog _ = () // The parameter is the title string.

let dummy format = slogf dummyLog format

let getNullLogger () =
    { new ILogger with

edit: Quelques remarques supplémentaires

La chaîne de format doit être un littéral. C'est parce que la chaîne littérale doit être lue au moment de la compilation afin de calculer la fonction qui doit être retournée pour engloutir les valeurs / types qui suivent la chaîne de format.

Par exemple, si vous faites printToConsole "% i% s% A% A" 7 "x" myType yourType , alors vous verrez int -> string -> MyType -> YourType dans la signature de printToConsole là où il est utilisé.

Il existe un moyen d'utiliser des chaînes simples comme chaînes de format avec ce système, mais je ne me souviens pas comment c'est fait, et de toute façon cela gâche le type sécurité. Cela est pratique lors de l'internationalisation de chaînes, et vos chaînes de format doivent provenir d'une ressource et non d'une source F # en raison de services de traduction externes.

edit 2: Wrap, par exemple système de journalisation

J'ai créé une interface à utiliser pour divers systèmes de journalisation, qui partagent à peu près les mêmes fonctionnalités.

    member _.Debugf format = slogf session.LogDebug format
    member _.Verbosef format = slogf session.LogVerbose format
    member _.Infof format = slogf session.LogMessage format
    member _.Warningf format = slogf session.LogWarning format
    member _.Errorf format = slogf session.LogError format
    member _.Fatalf format = slogf session.LogFatal format

...

    interface ILogger with

Ensuite, une implémentation pour mon système de journalisation actuellement utilisé ressemble à ceci.

type internal SiLogger(session: Session) =
    let slogf = Printf.ksprintf

...

    abstract member Debugf: StringFormat<'h, unit> -> 'h
    abstract member Verbosef: StringFormat<'h, unit> -> 'h
    abstract member Infof: StringFormat<'h, unit> -> 'h
    abstract member Warningf: StringFormat<'h, unit> -> 'h
    abstract member Errorf: StringFormat<'h, unit> -> 'h
    abstract member Fatalf: StringFormat<'h, unit> -> 'h

...

type ILogger =

Et il y a un enregistreur nul.

type DU = A | B
let i = 7
let s = "thirteen"
let du = B

printToConsole """an int %i and a string "%s" here""" i s
printToConsole """an int %i and a string "%s" and DU %A here""" i s du

// OUTPUT : an int 7 and a string "thirteen" here
// OUTPUT : an int 7 and a string "thirteen" and DU B here

// Note that OUTPUT is also part of the actual output,
// and it demonstrates how you can add e.g. a timestamp
// or line number or something to the output string, without
// it being part of the formatting.

...

let printToConsole format = Printf.ksprintf writeStringToConsole format

...

let writeStringToConsole (s: string) = Console.WriteLine ("OUTPUT : " + s)


4 commentaires

Existe-t-il un moyen de savoir combien d'arguments 'format' a? Je me demande s'il est possible de détecter printToConsole "hello" en tant qu'argument unique et d'imprimer la chaîne telle quelle, ce qui réduit le besoin de printToConsole "% s" "hello"


En passant, si vous faites cela pour vous connecter à un service de production, il y a plus à faire, alors cela et d'autres exigences apparaîtront. J'envisagerais l'utilisation d'une bibliothèque de journalisation F # pour cela. J'ai dû écrire LoggerWrapper.FSharp pour créer des fonctions de journalisation qui font les choses que vous recherchez probablement lorsque vous descendez le terrier du lapin. Par exemple, si la journalisation est désactivée, il ne devrait pas tenter de créer la chaîne / d'allouer de la mémoire pour des raisons de performances - ce qui est généralement vrai pour la journalisation de débogage. kprintf fait cela sans encapsuler le tout et donc cela peut être difficile à implémenter.


Je soupçonne que l'enregistreur nul que j'ai maintenant ajouté à la réponse fera un travail qui n'est pas nécessaire. Cela pourrait être un avantage si vous voulez la charge pendant le débogage, mais vous pouvez aussi, je suppose, ajouter quelque chose comme si activé, puis format factice . Ou simplement ne pas utiliser du tout ksprintf dans le journal nul.


Oh, et j'ai aussi des membres Debug, Verbose, etc., qui sortent simplement une chaîne sans utiliser de formatage. Pour des raisons de performances.