gpt4 book ai didi

generics - 具有灵活类型参数和返回值的函数?

转载 作者:行者123 更新时间:2023-12-01 08:41:00 24 4
gpt4 key购买 nike

我正在尝试编写一个函数,该函数接受某个类型或其任何子类型作为其参数之一,然后返回一个类型或其任何子类型的值。

[<AbstractClass>]
type Spreader () =
abstract Fn : unit -> unit

type Fire () =
inherit Spreader ()
override self.Fn () = ()

type Disease () =
inherit Spreader ()
override self.Fn () = ()

let spread (spr:#Spreader) : #Spreader =
match spr with
| :? Fire -> Fire ()
| :? Disease -> Disease ()
| _ -> failwith "I don't get it"

显然,这是行不通的,但是您会得到我正在尝试做的事情。

首先,我在Spreader类型中实现了一个抽象函数,并在子类型中对其进行了覆盖(覆盖?),但这需要向上转换,而我试图避免这种情况。

这可行吗?我正在研究泛型,但是我对它们的F#实现还不太了解。

编辑2010.07.08 1730 PST

关于我使用歧视工会的建议,我之前曾尝试过。我遇到的问题是,我定义为基本类型成员的任何函数都必须处理联合的每个分支。例如:
type strength = float32

type Spreader =
| Fire of strength
| Disease of strength

member self.Spread () =
match self with
| Fire str -> Fire str
| Disease str -> Disease str

member self.Burn () =
match self with
| Fire str -> Fire str
| _ -> failwith "Only fire burns"

传播功能在这里可以正常工作,但是如果我要燃烧火焰,那么我也必须提供疾病,这没有任何意义。

我想允许用户尝试做一些不合法的事情,例如尝试Disease.Burn,但是我不想在整个地方返回一堆选项类型,例如:
member self.Burn () =
match self with
| Fire str -> Some(Fire str)
| _ -> None

我宁愿只将Burn功能严格留给Fire Spreader,甚至不用为Disease Spreader定义。

此外,这也适用于属性。我希望Fire拥有一些对疾病没有意义的成员,反之亦然。另外,我希望能够使用点表示法来访问Spreader的强度值,因为无论如何我都将以这种方式访问​​其他成员,并且在已经定义了member.strength之后似乎有点多余。封装在对象中。 (例如|疾病str-> ...)

当然,另一种选择是简单地将Spread和Burn函数与Spreader类型分开,然后我必须(1)为函数提供丑陋的上载或泛型代码(如其他人所描述的那样),或者(2)具有完全独立的火灾和疾病函数,在Spread的情况下会很烂,因为我不得不将它们命名为SpreadFire和SpreadDisease(因为奇怪的是不允许外部类型的函数重载)。

作为F#的菜鸟,我欢迎所有批评和建议。 :)

编辑2010.07.09 0845 PST

乔恩·哈罗普(Jon Harrop):“您为什么要使用扩充和成员?”

因为实际代码中的类型是数学密集型的,所以我要在初始化时预先计算某些值并将它们存储为成员。

乔恩·哈罗普(Jon Harrop):“为什么只适用于Speader类型,为什么要适用于烧伤?”

如我所写,我不希望Burn应用于Spreader。我希望它仅适用于Fire。但是,在将功能实现为Spreader成员并将其与Spreader类型分离之间,我感到非常痛苦。 (不一致的气味。)

乔恩·哈罗普:“您的Fn成员的目的是什么?”

抱歉,这只是无关的代码,请忽略它。

乔恩·哈罗普(Jon Harrop):“您为什么使用抽象类而不是接口?”

代码的可读性和效率主要是。我讨厌只使用一种接口方法就必须改接口。

乔恩·哈罗普(Jon Harrop):“为什么将强度定义为float32的别名?”

代码可读性。

乔恩·哈罗普:“您要解决的实际具体问题是什么?!”

相关类型的代码共享,同时保持可读性。

有趣的是,您提供的解决方案恰好是我第一次尝试的解决方案(我在F#上待了大约一个星期),但是我将其丢弃,因为我对必须包装基本类型的想法感到不舒服DU中的一种解决方法,因为它无法重载类型定义之外定义的函数。我对使用基本类型走错路感到非常偏执(部分是由于VS2010中缺乏可悲的F#重构)。现在,函数式编程是我非常感兴趣的主题,而我几乎只是在四处寻求理解。

编辑2010.07.09 2230 PST

实际上,我/我开始不喜欢函数式编程(确实同意 http://briancarper.net/blog/315/functional-programming-hurts-me上的作者-如果我在F#教程或教科书中再看到一个斐波那契或阶乘编码示例,我将刺穿我发现的下一个书呆子) 。

我希望在座的人会回击我最新的反应(解释为什么我要做某些事情),并对乔恩表现出的这种蔑视(下)。我想向那些傲慢的精英学习FP,他们认为自己的代码没什么臭味。

最佳答案

您可能需要显式转换返回值:

let spread (spr: Spreader) =
match spr with
| :? Fire -> (Fire() :> Spreader)
| :? Disease -> (Disease() :> Spreader)

但是,这是一种非常不寻常的编程风格。您到底想做什么?

编辑

我仍然不明白您要完成什么。为什么要使用扩充和成员?为什么只将 burn应用于 Speader类型?您的 Fn会员的目的是什么?为什么使用抽象类而不是接口?为什么将 strength定义为 float32的别名?您要解决的实际具体问题是什么?

那这个呢:
type fire = ...

let burn fire = ...

type spreader = Disease | Fire of fire

let spread = function
| Disease -> ...
| Fire fire -> ...

基本上,如果您具有仅适用于一部分扩展器的功能,则需要以某种方式分离关注点。通过间接访问新类型(如上)或使用OOP并使类的子集实现一个接口。

关于generics - 具有灵活类型参数和返回值的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3208449/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com