gpt4 book ai didi

F# 计算表达式 : Can one be used to simplify this code?

转载 作者:行者123 更新时间:2023-12-01 08:22:48 25 4
gpt4 key购买 nike

我最近开始使用计算表达式来简化我的代码。到目前为止,对我唯一有用的是 MaybeBuilder,定义如下:

type internal MaybeBuilder() =

member this.Bind(x, f) =
match x with
| None -> None
| Some a -> f a

member this.Return(x) =
Some x

member this.ReturnFrom(x) = x

但我想探索其他用途。一种可能是我目前面临的情况。我有一些定义对称矩阵的供应商提供的数据。为了节省空间,只给出了矩阵的三角形部分,因为另一边只是转置。所以如果我在 csv 中看到一行

abc, def, 123

这意味着行 abc 和列 def 的值为 123。但是我不会看到这样的行

def, abc, 123

因为矩阵的对称性已经给出了这些信息。

我已将所有这些数据加载到 Map<string,Map<string,float>> 中我有一个函数可以为我获取任何条目的值,如下所示:

let myLookupFunction (m:Map<string,Map<string,float>>) s1 s2 =
let try1 =
match m.TryFind s1 with
|Some subMap -> subMap.TryFind s2
|_ -> None

match try1 with
|Some f -> f
|_ ->
let try2 =
match m.TryFind s2 with
|Some subMap -> subMap.TryFind s1
|_ -> None
match try2 with
|Some f -> f
|_ -> failwith (sprintf "Unable to locate a value between %s and %s" s1 s2)

既然我了解了计算表达式,我怀疑可以隐藏匹配语句。我可以像这样使用 MaybeBuilder 稍微清理一下

let myFunction2  (m:Map<string,Map<string,float>>) s1 s2 =
let maybe = new MaybeBuilder()
let try1 = maybe{
let! subMap = m.TryFind s1
return! subMap.TryFind s2
}

match try1 with
|Some f -> f
|_ ->
let try2 = maybe{
let! subMap = m.TryFind s2
return! subMap.TryFind s1
}
match try2 with
|Some f -> f
|_ -> failwith (sprintf "Unable to locate a value between %s and %s" s1 s2)

这样一来,我的匹配语句从 4 个减少到 2 个。是否有一种(非人为的)方法可以通过使用计算表达式进一步清理它?

最佳答案

首先,每次需要时创建一个新的MaybeBuilder 有点浪费。你应该这样做一次,最好是紧挨着 MaybeBuilder 本身的定义,然后在任何地方都使用同一个实例。这就是大多数计算构建器的工作方式。

第二:如果您只是将“try”逻辑定义为一个函数并重用它,您可以减少困惑的数量:

let myFunction2  (m:Map<string,Map<string,float>>) s1 s2 =
let try' (x1, x2) = maybe{
let! subMap = m.TryFind x1
return! subMap.TryFind x2
}

match try' (s1, s2) with
|Some f -> f
|_ ->
match try' (s2, s1) with
|Some f -> f
|_ -> failwith (sprintf "Unable to locate a value between %s and %s" s1 s2)

第三,注意您正在使用的模式:试试这个,如果不行就试试那个,如果不行就试试另一个,等等。模式可以抽象为函数(这就是全部!),所以让我们这样做:

let orElse m f = match m with
| Some x -> Some x
| None -> f()

let myFunction2 (m:Map<string,Map<string,float>>) s1 s2 =
let try' (x1, x2) = maybe{
let! subMap = m.TryFind x1
return! subMap.TryFind x2
}

let result =
try' (s1, s2)
|> orElse (fun() -> try' (s2, s1))

match result with
|Some f -> f
|_ -> failwith (sprintf "Unable to locate a value between %s and %s" s1 s2)

最后,我认为你的做法是错误的。您真正想要的似乎是具有两部分对称 key 的字典。那为什么不这样做呢?

module MyMatrix =
type MyKey = private MyKey of string * string
type MyMatrix = Map<MyKey, float>

let mkMyKey s1 s2 = if s1 < s2 then MyKey (s1, s2) else MyKey (s2, s1)


let myFunction2 (m:MyMatrix.MyMatrix) s1 s2 =
match m.TryFind (MyMatrix.mkMyKey s1 s2) with
| Some f -> f
| None -> failwith (sprintf "Unable to locate a value between %s and %s" s1 s2)

这里,MyKey 是一种封装一对字符串的类型,但保证这些字符串“按顺序”——即第一个字符串在字典序上比第二个字符串“少”。为了保证这一点,我将类型的构造函数设为私有(private),而是公开了一个函数 mkMyKey 来正确构造 key (有时称为 "smart constructor" )。

现在您可以自由地使用MyKey 来构建和查找 map 。如果你输入 (a, b, 42),你会得到 (a, b, 42)(b, a, 42).

旁白:我在您的代码中看到的一般错误是未能使用抽象。您不必在最低级别处理每条数据。该语言允许您定义更高级别的概念,然后根据它们进行编程。使用该能力。

关于F# 计算表达式 : Can one be used to simplify this code?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49699277/

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