gpt4 book ai didi

f# - 参数化/提取有区别的联合案例

转载 作者:行者123 更新时间:2023-12-04 14:56:46 24 4
gpt4 key购买 nike

目前我正在玩游戏并且经常使用 Event/Observables,我遇到的一件事是消除一些冗余代码,但我没有找到方法来做到这一点。为了解释它,让我们假设我们有以下 DU 和这个 DU 的 Observable。

type Health =
| Healed
| Damaged
| Died
| Revived

let health = Event<Health>()
let pub = health.Publish

我有很多这种结构。将所有“健康”消息组合在一起在某些情况下很有帮助和需要,但在某些情况下我只关心一个特殊的消息。因为这仍然经常需要我使用 Observable.choose将这些消息分开。然后我有这样的代码。
let healed = pub |> Observable.choose (function 
| Healed -> Some ()
| _ -> None
)

let damaged = pub |> Observable.choose (function
| Damaged -> Some ()
| _ -> None
)

编写这种代码实际上非常重复和烦人。我有很多这样的类型和消息。所以函数式编程的一个“规则”是“参数化所有的东西”。所以我写了一个函数 only只是帮助我。
let only msg pub = pub |> Observable.choose (function
| x when x = msg -> Some ()
| _ -> None
)

有了这样的功能,现在代码变得更短,编写起来也不那么烦人了。
let healed  = pub |> only Healed
let damaged = pub |> only Damaged
let died = pub |> only Died
let revived = pub |> only Revived

编辑:
要注意的重要事项。 healed , damaged , died , revived现在是 IObservable<unit> 类型不是 IObservable<Health> .这个想法不仅仅是分离消息。这可以通过 Observable.filter 轻松实现.这个想法是提取每个案例额外的数据。对于不携带任何额外数据的 DU 情况,这很容易,因为我只需要写 Some ()Observable.choose功能。

但这只是有效,只要 DU 中的不同情况不期望额外的值。不幸的是,我也有很多带有附加信息的案例。例如代替 HealedDamaged我有 HealedBy of int .所以一条消息还包含额外的东西得到了多少治愈。在这种情况下,我正在做的是这样的事情。
let healedBy = pub |> Observable.choose (function
| HealedBy x -> Some x
| _ -> None
)

但我真正想要的是把它写成这样
let healedBy = pub |> onlyWith HealeadBy

我期待的是得到一个 Observable<int> .我没有找到任何方法来做到这一点。我无法编写像 only 这样的函数以上。因为当我尝试评估 msg在模式匹配中,它只是被视为模式匹配所有情况的变量。我不能这样说:“匹配变量内的大小写。”

我可以检查变量是否属于某种特定情况。我可以做 if x = HealedBy then但在那之后,我无法从 x 中提取任何类型的数据.我真正需要的是类似“不安全”的提取选项,例如为它提供 optional.Value .有没有办法实现这样一个“onlyWith”函数来删除样板?

编辑:
这个想法不仅仅是分离不同的消息。这可以通过 Observable.filter 实现.这里 healedBy类型为 IObservable<int>不是 IObservable<Health>了。大想法是将消息分开 提取它携带的数据 在没有太多样板的情况下做到这一点。我已经可以用 Observable.choose 一次性分离和提取它目前。只要案例没有任何额外数据,我就可以使用 only功能来摆脱样板。

但是一旦一个案例有额外的数据,我就会回来写重复的 Observable.Choose函数并再次执行所有模式匹配。事情是目前我有这样的代码。
let observ = pub |> Observable.choose (function 
| X (a) -> Some a
| _ -> None
)

我有很多消息和不同类型的东西。但唯一改变的是其中的“X”。所以我显然想参数化“X”,这样我就不必一次又一次地编写整个构造。充其量应该是
let observ = anyObservable |> onlyWith CaseIWantToSeparate

但是新的 Observable 是我分离的特定案例的类型。不是 DU 本身的类型。

最佳答案

您正在寻找的行为不存在,它在您的第一个示例中运行良好,因为您始终可以始终如一地返回 unit option .

let only msg pub = 
pub |> Observable.choose (function
| x when x = msg -> Some ()
| _ -> None)

请注意,它的类型为: 'a -> IObservable<'a> -> IObservable<unit>
现在,让我们想象一下,为了创建一个清晰的示例,我定义了一些可以包含多种类型的新 DU:
type Example =
|String of string
|Int of int
|Float of float

想象一下,作为一个思考练习,我现在尝试定义一些与上述相同的通用函数。它的类型签名可能是什么?
Example -> IObservable<Example> -> IObservable<???>

??? 不能是上述任何具体类型,因为类型都不同,也不能是通用类型,原因相同。

由于不可能为这个函数提出一个合理的类型签名,这是一个非常强烈的暗示,这不是这样做的方法。

您遇到的问题的核心是您无法在运行时决定返回类型,返回的数据类型可以是几种不同的可能但已定义的情况,这正是区分联合帮助您解决的问题。

因此,您唯一的选择是明确处理每种情况,您已经知道或已经看到了如何执行此操作的几种选择。就个人而言,我认为定义一些要使用的辅助函数并没有什么可怕的:
let tryGetHealedValue = function
|HealedBy hp -> Some hp
|None -> None

let tryGetDamagedValue = function
|DamagedBy dmg -> Some dmg
|None -> None

关于f# - 参数化/提取有区别的联合案例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35537160/

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