首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通用高阶函数

通用高阶函数
EN

Stack Overflow用户
提问于 2011-08-27 17:16:25
回答 5查看 1.4K关注 0票数 7

当我将泛型函数作为局部值传递,而不是作为参数传递时,我可以使用具有不同类型参数的泛型函数,这有什么原因吗?例如:

代码语言:javascript
复制
let f = id

let g (x,y) = (f x, f y)

g ( 1, '2')

运行良好,但如果我尝试将函数作为参数传递

代码语言:javascript
复制
let g f (x,y) = (f x, f y)

g id ( 1, '2')

它失败是因为它接受版本f< int >,并尝试应用它两次。

我已经找到了一个变通方法,但它迫使我编写两倍于所传递函数的代码:

代码语言:javascript
复制
let g f1 f2 (x,y) = (f1 x, f2 y)

g id id ( 1, '2')

这是我第二次面对这个问题。

为什么它会这样,如果函数是一个局部值,或者如果它是作为参数传递的,那么它就不应该是相同的?

有没有一种方法可以在不复制函数的情况下做到这一点?

一个黑客,也许使用显式类型约束,内联魔术,引用?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-08-29 06:04:14

这是内联的魔术。让我们以kvb的代码为例,定义一个处理所有情况的gmap函数:

代码语言:javascript
复制
let inline gmap f (x, y) = f $ x, f $ y

type One = One with static member ($) (One, x) = 1  // Example1 ConvertAll
type Id  = Id  with static member ($) (Id , x) = x  // Example2 PassThrough

type SeqSingleton  = SeqSingleton  with static member ($) (SeqSingleton , x) = seq [x]
type ListSingleton = ListSingleton with static member ($) (ListSingleton, x) = [x]
type ListHead      = ListHead      with static member ($) (ListHead, x) = List.head x

// Usage
let pair1 = gmap One ("test", true)
let pair2 = gmap Id  ("test", true)
let pair3 = gmap SeqSingleton  ("test", true)
let pair4 = gmap ListSingleton ("test", true)
let pair5 = gmap ListHead (["test";"test2"], [true;false])

let pair6 = ("test", true) |> gmap ListSingleton |> gmap ListHead

(* results
val pair1 : int * int = (1, 1)
val pair2 : string * bool = ("test", true)
val pair3 : seq<string> * seq<bool> = (["test"], [true])
val pair4 : string list * bool list = (["test"], [true])
val pair5 : string * bool = ("test", true)
val pair6 : string * bool = ("test", true)
*)

更新

也可以使用更通用的gmap函数定义的here,然后它也可以处理n元组(n < 9)。

票数 11
EN

Stack Overflow用户

发布于 2011-08-28 22:32:44

正如rkhayrov在评论中提到的,当你可以有更高等级的类型时,类型推断是不可能的。在您的示例中,您有

代码语言:javascript
复制
let g f (x,y) = (f x, f y)

这里有两种可能的g类型,它们是不兼容的(用一种混合的F#/Haskell语法编写的):

  1. forall 'b,'c,'d. ((forall 'a . 'a -> 'b) -> 'c * 'd -> 'b)
  2. forall 'c,'d. (forall 'a . 'a -> 'a) -> 'c * 'd -> 'c * 'd)

给定第一种类型,我们可以调用g (fun x -> 1) ("test", true)并获取(1,1)。对于第二种类型,我们可以调用g id ("test", true)并获取("test", true)。这两种类型都不比另一种更通用。

如果你想在F#中使用排名更高的类型,你可以,但是你必须明确的使用一个中间的名义类型。以下是对上述每种可能性进行编码的一种方法:

代码语言:javascript
复制
module Example1 = 
    type ConvertAll<'b> =
        abstract Invoke<'a> : 'a -> 'b

    let g (f:ConvertAll<'b>) (x,y) = (f.Invoke x, f.Invoke y)

    //usage
    let pair = g { new ConvertAll<int> with member __.Invoke(x) = 1 } ("test", true)

module Example2 = 
    type PassThrough =
        abstract Invoke<'a> : 'a -> 'a

    let g (f:PassThrough) (x,y) = (f.Invoke x, f.Invoke y)

    //usage
    let pair = g { new PassThrough with member __.Invoke(x) = x } ("test", true)
票数 7
EN

Stack Overflow用户

发布于 2011-08-27 17:30:36

我猜这是预期的行为:

在第一种情况下,您调用两个不同版本的f (一个使用int,另一个使用char),在第二种情况下,您对这两个版本都使用相同的版本,编译器会推断它(上下左右-还记得吗?)成为int->int

问题是,编译器会将通用版本翻译成具体的版本。我看不到解决这个问题的办法--即使是用inline也不行,但也许有人可以在这里施展魔法;)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7213599

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档