Как написать пользовательскую версию F # printfn, которая принимает переменное количество аргументов? - PullRequest
0 голосов
/ 29 августа 2018

Я хочу написать расширенную версию функции F # printfn, которая печатает текущее время в дополнение к тексту. Примерно так:

let printimefn fmt =
    let time = DateTime.Now
    let strtime = sprintf "%02d:%02d:%02d" time.Hour time.Minute time.Second
    printfn "%s %A"  strtime fmt

К сожалению, это не работает, как ожидалось. Аргумент "fmt" теряет свой тип Printf.TextWriterFormat <'T>.

Я могу форсировать его тип с помощью аннотации типа:

let printimefn (fmt : Printf.TextWriterFormat<'T>) =
    let time = DateTime.Now
    let strtime = sprintf "%02d:%02d:%02d" time.Hour time.Minute time.Second
    printfn "%s %A"  strtime fmt

Но тогда результат printimefn становится единицей, а не 'T. Так что это все еще не работает. Интересно, как правильно написать пользовательскую функцию printfn.

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Я хочу продемонстрировать это, переместив показания времени из функции по двум причинам. Прежде всего, чтобы продемонстрировать, как добавить один или несколько аргументов, которые не используются ksprintf, но также потому, что у нас должна быть функция, которая не имеет побочных эффектов. Если вы хотите, чтобы время считывалось внутри функции, вместо этого создайте функцию-оболочку, которая считывает время, а затем вызывает следующую функцию.

Сначала есть два явных аргумента, которые не передаются в ksprintf. Затем следует неявно строка формата и любые неявные аргументы, требуемые для строки формата.

let tsprintf (dateTime: DateTime) (tag: string) =
    let dateTimeString =
        dateTime.ToString (@"yyyy/MM/dd HH:mm:ss",
            System.Globalization.CultureInfo.InvariantCulture)
    Printf.ksprintf (fun formatString ->
        "[" + dateTimeString + " <" + tag + ">] " + formatString)

let testTime = DateTime(1987, 12, 31, 11, 59, 58)

let message = tsprintf testTime "007" "Hello %s!" "James Bond"

message.Dump() // LINQPad output : [1987/12/31 11:59:58 <007>] Hello James Bond!

InvariantCulture в сочетании с выбранной строкой формата времени гарантирует, что результат, который вы видите, точно соответствует тому, что содержится в комментарии в конце, независимо от того, где и что.

Причина, по которой я использую DateTime.ToString, заключается в том, что он форматирует DateTime любым удобным для вас способом. Форматирование в ksprintf не кажется хорошей идеей.

Аргументы, не используемые ksprintf, явно объявлены в tsprintf. Строка формата и следующие аргументы для ksprintf не объявляются явно, но они следуют всем явно объявленным аргументам.

0 голосов
/ 29 августа 2018

Это можно сделать с помощью Printf.ksprintf

let printimefn fmt =
    let time = DateTime.Now
    Printf.ksprintf (
        fun s ->
            printfn "%02d:%02d:%02d %s" time.Hour time.Minute time.Second s)
        fmt
...