f# - 针对 PrintfFormat 编写单元测试
问题描述
我有一种类型,我试图通过编写单元测试来理解它,但是我无法推理如何处理 PrintfFormat
type ValueFormat<'p,'st,'rd,'rl,'t,'a> = {
format: PrintfFormat<'p,'st,'rd,'rl,'t>
paramNames: (string list) option
handler: 't -> 'a
}
with
static member inline construct (this: ValueFormat<_,_,_,_,_,_>) =
let parser s =
s |> tryKsscanf this.format this.handler
|> function Ok x -> Some x | _ -> None
let defaultNames =
this.format.GetFormatterNames()
|> List.map (String.replace ' ' '_' >> String.toUpperInvariant)
|> List.map (sprintf "%s_VALUE")
let names = (this.paramNames ?| defaultNames) |> List.map (sprintf "<%s>")
let formatTokens = this.format.PrettyTokenize names
(parser, formatTokens)
我有信心我可以解决所有问题,但 PrintfFormat 正在向我抛出所有这些泛型。
我正在查看要单元测试的代码的文件是FSharp.Commandline 框架。
我的问题是,什么是 PrintfFormat 以及应该如何使用它?
printf.fs 文件的链接在这里。它包含 PrintfFormat 的定义
解决方案
F# 源代码中定义的类型PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>
有四个类型参数:
'Result
是您的格式化/解析函数产生的类型。这是string
为了sprintf
'Printer
是一种基于格式字符串生成的函数类型,例如"%d and %s"
会给你一个函数类型int -> string -> 'Result
'Tuple
是基于格式字符串生成的元组类型,例如"%d and %s"
会给你一个元组类型int * string
。'State
并且'Residue
是当您使用自定义格式化程序时使用的类型参数%a
,但为了简单起见,我现在将忽略它(除非您有%a
格式字符串,否则永远不需要它)
有两种使用类型的方法。无论是格式化,在这种情况下,您都需要编写一个返回'Printer
结果的函数。困难在于您需要使用反射构造返回函数。这是一个仅适用于一种格式字符串的示例:
open Microsoft.FSharp.Reflection
let myformat (fmt:PrintfFormat<'Printer,obj,obj,string,'Tuple>) : 'Printer =
unbox <| FSharpValue.MakeFunction(typeof<'Printer>, fun o ->
box (o.ToString()) )
myformat "%d" 1
myformat "%s" "Yo"
这只是返回作为%d
or的值传递的参数%s
。要使其适用于多个参数,您需要递归地构造函数(这样它不仅是例如int -> string
,而且是int -> (int -> string)
)
在另一种用途中,您定义一个返回的函数,'Tuple
它需要根据指定的格式化字符串创建一个包含值的元组。这是一个仅处理%s
和%d
格式化字符串的小示例:
open FSharp.Reflection
let myscan (fmt:PrintfFormat<'Printer,obj,obj,string,'Tuple>) : 'Tuple =
let args =
fmt.Value
|> Seq.pairwise
|> Seq.choose (function
| '%', 'd' -> Some(box 123)
| '%', 's' -> Some(box "yo")
| _ -> None)
unbox <| FSharpValue.MakeTuple(Seq.toArray args, typeof<'Tuple>)
myscan "%d %s %d"
推荐阅读
- java - CORS Origin 失败的 Spring Boot
- javascript - 如何使用文本创建 td 边框
- sql - SQL如何使用JOIN和GROUPBY显示一个MAX函数的匹配情况
- java - Spring Kafka、Spring Cloud Stream 和 Avro 兼容性未知的魔法字节
- postgresql - 将除法的其余部分放入下一行
- java - NoSuchMethodError:org.springframework.beans.factory.config.ConfigurableListableBeanFactory
- windows-10 - 安装 Oracle 12c 客户端
- python - 重命名数据框中的列名称,该列名称与旧名称具有相似的字符串
- r - 分配包含 R 中原始名称的标识符值
- gradle-release-plugin - 如何使用 gradle-release-plugin 自动增加次要 - 而不是增量