假设我要使用sprintf
的别名,我可以这样做:
namespace FSharp
module Core =
let specialsprintf x y =
sprintf x y
这将为我带来sprintf相同的编译时优势(与其C#表亲API System.String.Format
相比),例如类型检查,检查传递的参数数量是否正确等。
但是,假设我要禁用此编译时功能,并通过在下面调用String.Format来编写sprintf的简单版本。这可能吗?我知道目标听起来很愚蠢,但我想进行一下心理锻炼以确保我了解F#键入在这里的工作原理。如果我这样做(假设我们只能传递一个参数):
namespace FSharp
module Core =
let specialsprintf x y =
#if NORMAL_FSHARP
sprintf x y
#else
let x = x.Replace("%s", "{0}")
System.String.Format(x,y)
#endif
它甚至没有编译,错误是:
~/FSharpPlayground.fs(17,17): Error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. (FS0072) (FSharpPlayground)
嗯,为什么?
好吧,如果我指定这样的类型:
namespace FSharp
module Core =
let specialsprintf
#if NORMAL_FSHARP
x
#else
(x: string)
#endif
y =
#if NORMAL_FSHARP
sprintf x y
#else
let x = x.Replace("%s", "{0}")
System.String.Format(x,y)
#endif
然后我在调用方中遇到编译错误:
~/FSharpPlaygroundUse.fs(48,48): Error FS0001: This expression was expected to have type 'obj []' but here has type 'string' (FS0001)
我想我现在需要立即限定y
的类型,但是不确定如何扩展以使其能够使用2个参数而不是仅使用1个参数(我不知道)设法使其与ParamArray
属性配合使用)。某事告诉我,我可能还需要一个uncurry
函数,但我有点迷失了:-/
答案 0 :(得分:1)
我假设您想要的是类似sprintf
的东西,但是杂乱无章,并且没有类型检查格式。
不幸的是,F#没有'uncurrying'或可变参数函数。
话虽这么说,但也可以使用参数数组。 ParamArray
仅对类成员有效,对绑定无效,因此我们可以选择static member
,其范围类似于let fn () =
。
type SpecialPrint =
static member sprintf (format, [<ParamArray>] args) =
let index = ref -1
let stringFormat = Regex.Replace(format, "%[a-z]", (fun _ -> sprintf "{%d}" (Interlocked.Increment index)))
String.Format(stringFormat, args)
使用;
let result = SpecialPrint.sprintf ("Hello %s%s", "World", "!") //Hello World!