Как настроить вывод пользовательского типа с помощью printf?

Я прочитал большую часть Expert F# и работаю над созданием реального приложения. Во время отладки я привык передавать команды fsi, подобные этой, чтобы сделать вещи разборчивыми в окне repl:

fsi.AddPrinter(fun (x : myType) -> myType.ToString())

Я хотел бы расширить это для работы с форматером printf, чтобы я мог ввести, например.

printf "%A" instanceOfMyType 

и управлять выводом для пользовательского типа. В книге подразумевается, что это можно сделать (стр. 93, «Универсальное структурное форматирование может быть расширено для работы с любыми определяемыми пользователем типами данных, тема рассматривается на веб-сайте F#»), но мне не удалось найти никаких ссылок на то, как на самом деле добиться этого. Кто-нибудь знает, как? Это вообще возможно?

Изменить:

Я должен был включить образец кода, это тип записи, с которым я имею дело, например.

type myType = 
    {a: int}        
    override m.ToString() = "hello"

let t = {a=5}
printfn "%A" t
printfn "%A" (box t)  

оба оператора печати дают:

{a = 5;}

person flatline    schedule 26.04.2009    source источник


Ответы (3)


Похоже, правильный способ сделать это в F# 2.0 — использовать атрибут StructuredFormatDisplay для пример:

[<StructuredFormatDisplay("hello {a}")>]
type myType = {a: int}

В этом примере вместо {a = 42;} по умолчанию вы получите hello 42.

Это работает одинаково для типов объектов, записей и объединений. И хотя шаблон должен быть в формате "PreText {PropertyName} PostText" (PreText и PostText являются необязательными), на самом деле он более эффективен, чем ToString(), потому что:

  1. PropertyName может быть свойством любого типа. Если это не строка, то она также подлежит структурированному форматированию. Блог Дона Сайма дает пример рекурсивного форматирования дерева таким образом.

  2. Это может быть вычисляемое свойство. Таким образом, вы можете заставить ToString() работать с типами записи и объединения, хотя и довольно окольным путем:

    [<StructuredFormatDisplay("{AsString}")>]
    type myType = 
        {a: int}
        override m.ToString() = "hello"
        member m.AsString = m.ToString()  // a property that calls a method
    

Кстати, ToString() будет использоваться всегда (даже для типов записей и объединений), если вы вызовете printfn "%O" вместо printfn "%A".

person Todd Owen    schedule 23.11.2012
comment
Кстати, спасибо @Brian за публикацию этих ссылок в последующем комментарии к его ответу. Я просто подумал, что стоит уточнить для всех, кто придет искать. - person Todd Owen; 24.11.2012
comment
Свойство/член может быть private, чтобы избежать раскрытия возможного объекта, выделяющего свойства объекта (а не как вызовы метода). Кроме того, я не смог использовать методы объекта (не был найден). - person Henrik; 17.10.2018

Хм... Я смутно припоминаю какие-то изменения, но не помню, произошли ли они до или после CTP (1.9.6.2).

Во всяком случае, на ОСАГО я вижу, что

type MyType() =
    override this.ToString() = "hi"
let x = new MyType()
let xs = Array.create 25 x
printfn "%A" x
printfn "%A" xs

при оценке в окне VFSI делает то, что я хотел бы, и это

x;;
xs;;

тоже хорошо печатает. Итак, я думаю, мне неясно, чем это отличается от желаемого?

person Brian    schedule 26.04.2009
comment
Спасибо; см. мое редактирование исходного сообщения, это тип записи с добавленной функцией-членом, и он ведет себя иначе, чем тип класса... - person flatline; 27.04.2009
comment
@ Брайан, да, это должно работать, но, как говорит Flatline, это не работает с типами объединения и записи. Я столкнулся с этим некоторое время назад: cs.hubfs.net/forums/post/9163. aspx (не могу вспомнить, отправлял ли я что-то в fsbugs, когда не получил ответа, извините) - person Kurt Schelfthout; 27.04.2009

Если вы переопределите метод ToString, это должно сработать.

person Codingday    schedule 26.04.2009