当前位置:  开发笔记 > 编程语言 > 正文

具有通用参数类型的函数

如何解决《具有通用参数类型的函数》经验,为你挑选了4个好方法。

我试图弄清楚如何定义一个适用于多种类型参数的函数(例如int和int64).据我了解,F#中无法实现函数重载(当然编译器会抱怨).以下面的功能为例.

let sqrt_int = function
    | n:int   -> int (sqrt (float n))
    | n:int64 -> int64 (sqrt (float n))

编译器当然抱怨语法无效(似乎不支持模式匹配中的类型约束),尽管我认为这说明了我想要实现的内容:一个对多个参数类型进行操作并返回相应值的函数类型.我觉得在F#中使用泛型类型/类型推断/模式匹配的某种组合是可能的,但语法已经躲过了我.我也尝试过使用:?操作者(动态型测试)和在模式匹配块子句,但这仍然会产生各种错误.

由于我对这门语言不熟悉,我很可能会尝试在这里做一些不可能的事情,所以如果有其他解决方案,请告诉我.



1> Brian..:

重载通常是类型推断语言的问题(至少在类似F#的情况下,类型系统不足以包含类型类).你在F#中有很多选择:

在方法(类型成员)上使用重载,在这种情况下,重载的工作方式与其他.Net语言非常相似(您可以使用ad-hoc重载成员,提供的调用可以通过参数的数量/类型来区分)

使用"inline","^"和静态成员约束对函数进行ad-hoc重载(这是大多数需要处理int/float/etc的各种数学运算符;这里的语法很奇怪,这是除了F#库之外很少使用

通过传递额外的操作字典参数来模拟类型类(这是INumeric在其中一个F#PowerPack库中执行的操作,用于为任意用户定义的类型推广各种数学算法)

回退到动态类型(传入'obj'参数,进行动态类型测试,为坏类型抛出运行时异常)

对于您的特定示例,我可能只使用方法重载:

type MathOps =
    static member sqrt_int(x:int) = x |> float |> sqrt |> int
    static member sqrt_int(x:int64) = x |> float |> sqrt |> int64

let x = MathOps.sqrt_int 9
let y = MathOps.sqrt_int 100L


@Jon Harrop:这个问题不是一个引人注目的例子吗?我期望现代语言会有这个功能.该函数重载应该在每个语言设计者列表中.这是可以解决的问题,但是为什么!它应该工作.
@Gorgen:我不相信,不,不.问题是,从来没有"只是工作".使用C++和C#等语言进行重载可以防止类型推断.使用类型类等功能在Haskell等语言中进行重载可能会在您最不期望的情况下大幅降低基本算术运算的性能.标准ML选择ad-hoc多态来覆盖单个特殊情况(int vs float)但不包括其他(例如标量与向量).OCaml根本没有超载,但最近采用了称为"分隔"重载的解决方案.
Scala和C#3选择了"本地"类型推理的妥协,这只是蹩脚的.F#选择了一些重载(算术运算符)的实用折衷方案,但是在一个保证静态解析并且没有调度的框架中,所以它总是很快.这些权衡是众所周知的,但尚未解决.我个人的偏好是F#解决方案,因为它解决了最重要的重载(算术)而没有引入可怕的性能特征,而它们通常最重要(算术!).

2> Gus..:

这有效:

type T = T with
    static member ($) (T, n:int  ) = int   (sqrt (float n))
    static member ($) (T, n:int64) = int64 (sqrt (float n))

let inline sqrt_int (x:'t) :'t = T $ x

它使用静态约束和重载,这使得对参数类型进行编译时查找.

静态约束是在运算符($本例中为运算符)的情况下自动生成的,但始终可以手动编写:

type T = T with
    static member Sqr (T, n:int  ) = int   (sqrt (float n))
    static member Sqr (T, n:int64) = int64 (sqrt (float n))

let inline sqrt_int (x:'N) :'N = ((^T or ^N) : (static member Sqr: ^T * ^N -> _) T, x)

更多关于这里.


感谢@Abel,我确实详细介绍了该解决方案,并提供了一个包含更多详细信息的博客条目的链接.Mauricio的答案采用了一种非常不同的方法,它也是有效的,它通过从"浮点数"转换为所有类型的代码总是相同的代码,但是如果你想使用大整数,你可能会遇到一个限制.

3> Mauricio Sch..:

是的,这可以做到.看看这个hubFS线程.

在这种情况下,解决方案是:

let inline retype (x:'a) : 'b = (# "" x : 'b #)
let inline sqrt_int (n:'a) = retype (sqrt (float n)) : 'a

警告:没有编译时类型检查.即sqrt_int "blabla"编译好,但你会在运行时得到一个FormatException.



4> Brian..:

这是使用运行时类型检查的另一种方式......

let sqrt_int<'a> (x:'a) : 'a = // '
    match box x with
    | :? int as i -> downcast (i |> float |> sqrt |> int |> box)
    | :? int64 as i -> downcast (i |> float |> sqrt |> int64 |> box)
    | _ -> failwith "boo"

let a = sqrt_int 9
let b = sqrt_int 100L
let c = sqrt_int "foo" // boom

推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有