F# 我的匹配看起来很笨拙,有没有更好的编码方法?

F# 我的匹配看起来很笨拙,有没有更好的编码方法?,f#,pattern-matching,option,F#,Pattern Matching,Option,这似乎有效,但似乎笨重。有没有更好的编码方法 // Hunting for the best index to use for a data compare let getIndex connDB strTable = match getIndexByPrimaryKey connDB strTable with | Some(name) -> Some(name) | None -> match getIndexByCluster connDB

这似乎有效,但似乎笨重。有没有更好的编码方法

// Hunting for the best index to use for a data compare
let getIndex connDB strTable =
    match getIndexByPrimaryKey connDB strTable with
    | Some(name) -> Some(name)  
    | None ->
    match getIndexByCluster connDB strTable with
    | Some(name) -> Some(name)
    | None -> 
    match getIndexByFirstColumns connDB strTable with
    | Some(name) -> Some(name)
    | None -> 
    match getIndexByOnlyUnique connDB strTable with
    | Some(name) -> Some(name)
    | None -> 
    match getAnyUniqueIndex connDB strTable with
    | Some(name) -> Some(name)
    | None -> None

最简洁的版本可能是使用:

让getIndex connDB标准表=
[getIndexByPrimaryKey;
getIndexByCluster;
getIndexByFirstColumns;
getIndexByOnlyUnique;
getAnyUniqueIndex;]
|>List.tryPick(fun fn->fn connDB strTable)
更新:

该技术可以很容易地扩展到使用闭包,因此它适用于具有不同参数的函数(具有许多
fun
s:-):

让getIndex connDB标准表=
[fun()->getIndexByPrimaryKey可连接数据库;
fun()->getIndexByCluster连接数据库stTable;
fun()->getIndexByFirstColumns connDB strTable;
fun()->getIndexByOnlyUnique connDB strTable;
fun()->getAnyUniqueIndex connDB strTable;]
|>List.tryPick(fun-fn->fn())

我将编写一个
orElse
函数。然后你可以这样做:

让orElse f=函数
|无->f()
|一些uu如x->x
让getIndex connDB标准表=
getIndexByPrimaryKey连接数据库标准表
|>orElse(fun()->getIndexByCluster连接数据库标准表)
|>orElse(fun()->getIndexByFirstColumns connDB strTable)
|>orElse(fun()->getIndexByOnlyUnique connDB标准表)
|>orElse(fun()->getAnyUniqueIndex connDB strTable)
或者,如果您更喜欢“工作流”语法(并且经常需要),请执行以下操作:

模块OrElse=
设bind f=函数
|无->f()
|一些x->一些x
让我们合并m1和m2=
m1 |>bind(fun()->m2)
类型OrElseBuilder()=
成员x.Zero()=无
成员x.Return(v)=一些v
成员x.Bind(m,f)=Bind f m
成员x.ReturnFrom(m)=m
构件x.联合收割机(m1,m2)=联合收割机m1,m2
成员x.Delay(f)=f()
设orElse=OrElseBuilder()
将让您更清楚地陈述:

openorelse
奥莱尔斯{
return!getIndexByPrimaryKey可连接数据库
return!getIndexByCluster connDB标准表
return!getIndexByFirstColumns connDB strTable
return!getIndexByOnlyUnique connDB标准表
return!getAnyUniqueIndex connDB strTable
}
由于将相同的参数传递给每个函数,所以pad的解决方案可能会尽可能简洁。这些解决方案解决了将嵌套的
match x替换为一些u-as-v->v | None->…
的一般问题

编辑

扩展Tomas的想法,您可以使用通用的活动模式来“模式化”函数:

let(| FN | | |)fx=fx
然后做:

匹配connDB,可与
|FN getIndexByPrimaryKey name->Some name
|FN getIndexByCluster名称->某些名称
|FN getIndexByFirstColumns name->Some name
|FN getIndexByOnlyUnique name->Some name
|参数->getAnyUniqueIndex参数

这需要对函数做一点小小的更改:参数必须是元组形式。

我认为最干净的选择是将
getIndexByz
操作定义为活动模式,而不是函数。然后可以编写以下模式匹配:

let getIndex connDB strTable = 
    match connDB, strTable with
    | IndexByPrimaryKey name 
    | IndexByCluster name 
    | IndexByFirstColumns name 
    | IndexByOnlyUnique name 
    | AnyUniqueIndex name -> Some(name)   
如果您仍然希望在没有模式匹配的上下文中使用程序其他部分中的函数,则可以将活动模式定义为简单包装:

let (|IndexByPrimaryKey|_|) (connDB, strTable) =
  getIndexByPrimaryKey connDB strTable

遗憾的是,没有办法“自动”将函数转换为活动模式但我认为,如果您需要表达一些业务逻辑并希望其可读性,那么额外的努力是值得的。

我打赌您可以在管道中使用Option.bind:if应该具有相反的含义-询问下一个处理程序current Fails是否对此进行了测试,并且它看起来确实很懒。在本例中,从未达到t3上的断点。让t1()=None让t2()=Some(2+2)让t3()=Some(3+3)让test=[t1;t2;t3]>List.tryPick(fun-fn->fn())让t3()=Some(3+3)让test=[t1;t2;t3]>List.tryPick(fun-fn->())是的,它不必遍历整个列表。请参见
List.tryPick
implementation h。这绝对是最短的,因为每个函数都传递了相同的参数。丹尼尔:我在回答中使用闭包解决了一般问题。当然,在本例中没有必要这样做,我认为定义不遵循任何标准模式的特殊计算表达式只会使代码非常混乱。这实际上是对该功能的错误使用,与创建@pad的答案中的函数列表相比,它没有任何优势。只是好奇,您如何定义这里的“标准”?如果定义计算表达式不受语言的限制,那么如何确定什么是可接受的呢?一种更具原则性的方法是,您的
orElse
计算生成器不遵守任何常见的monad法则,因此它可能是错误的用法。我对此不太敏感,但当您的
bind
有一个错误的类型时,它有点可怕:-)。现在,我认为有一种方法可以用一个计算表达式来表达这类事情,但是1)使用一个不同的,不太特别的表达式,2)在这种情况下它并没有真正的帮助。当然,我并不是说我的名单是正确的。但是,它应该给你一个想法,我认为一个令人信服的定义。如果您认为该文件缺少一些有用的可重用结构,那么请随意记录它,我很乐意将其添加到其中。(顺便说一句:我想已经有一个适合这个目的,但仍然可以用于其他类型。)我喜欢你的
orElse
功能;我经常希望在选项模块中有类似的功能。它不是“自动”的,但我在回答中包含了一种在活动模式中调用函数的方法。