上周有人向我指出了F#中的活动模式。 这是一个非常有趣的发现。
活动模式用于F#上以对数据进行分区。 这些分区然后可以与模式匹配一起使用。 微软的网页将它们与有区别的工会进行了比较。
如何使用它们? 好吧,您可以看一下上面的链接,或者只是关注这篇文章(或者,实际上两者都做)。
下面是使用模式匹配的平凡函数的初始版本。
let thisNumberTrait(number) =
match number with
| x when x = 0 -> "Is Zero!!"
| x when x > 0 -> "Is Positive!!"
| x when x < 0 -> "Is Negative!!"
| _ -> "I shouldn't be here"
上面代码的第一个改进是将逻辑放在功能后面的防护上,因此我们可以这样做:
let isZero(number) = number = 0
let isPositive(number) = number > 0
let isNegative(number) = number < 0
let thisNumberTrait(number) =
match number with
| x when isZero(x) -> "Is Zero!!"
| x when isPositive(x) -> "Is Positive!!"
| x when isNegative(x) -> "Is Negative!!"
| _ -> "I shouldn't be here"
但是,如果我们可以完全删除保护条款呢? 这就是活动模式起作用的地方。
let (|Zero|_|) (number) = if number = 0 then Some(number) else None
let (|Positive|_|) (number) = if number > 0 then Some(number) else None
let (|Negative|_|) (number) = if number < 0 then Some(number) else None
let thisNumberTrait(number) =
match number with
| Zero(x) -> "Is Zero!!"
| Positive(x) -> "Is Positive!!"
| Negative(x) -> "Is Negative!!"
| _ -> "I shouldn't be here"
如您所见,代码上有一些区别。 如果我们专注于逻辑函数,我们可以看到现在函数的名称已针对构造(| | |)
进行了更改,现在返回的是Option类型。 然后,模式匹配将x when ...
代码替换为新的Pattern Zero(x)
。 我们使功能复杂一些,简化了模式匹配。
如果我们看看提供的答案是什么,我们就会意识到我们根本没有使用数字,因此实际上我们可以更改行号
| Zero(x) -> "Is Zero!!"
对于
| Zero(_) -> "Is Zero!!"
x
不是我们传递给Pattern的数据,而是返回的数据! 我们通过了数据隐含性。 但是,等等,如果我不使用返回数据,是否需要全部返回? 答案是否定的。 所以现在我们替换
let (|Zero|_|) (number) = if number = 0 then Some(number) else None
与
let (|Zero|_|) (number) = if number = 0 then Some(Zero) else None
我们返回模式的名称,而不是数据。
现在这意味着在比赛中我们可以从
| Zero(_) -> "Is Zero!!"
至
| Zero -> "Is Zero!!"
到目前为止,我们已经返回了已传递的相同数据,但未返回任何内容。 但是,如果我们想返回其他类型的数据怎么办? 可能的:请看下面的(|Positive|_|)
现在返回一个字符串。
let (|Zero|_|) (number) = if number = 0 then Some(Zero) else None
let (|Positive|_|) (number) = if number > 0 then Some("Positive") else None
let (|Negative|_|) (number) = if number < 0 then Some(number) else None
let thisNumberTrait(number) =
match number with
| Zero -> "Is Zero!!"
| Positive(x) -> sprintf "Is %s!!" x
| Negative(_) -> "Is Negative!!"
| _ -> "I shouldn't be here"
到目前为止,我一直在使用部分模式。 即,不能将数据定义为分区。 这就是为什么下划线( (|Zero|_|)
)以及为什么我们返回Option(Some | None)的原因。 但是我们可以有一个完整的活动模式,其中数据必须位于分区之一内。 更改很容易,如下所示)
let (|Zero|_|) (number) = if number = 0 then Some(Zero) else None
至
let (|Zero|NonZero|) (number) = if number = 0 then Zero else NonZero
可以使用&
组合模式和|
来组合模式|
为或组合。
所以代替
| Positive(x) -> sprintf "Is %s!!" x
我们可以写
| NonZero & Positive(x) -> sprintg "Is %s!!" x
并非在这种情况下有任何区别,而是可能的。
最后,我们可以制作一个大图案,替换
let (|Zero|_|) (number) = if number = 0 then Some(Zero) else None
let (|Positive|_|) (number) = if number > 0 then Some(Positive) else None
let (|Negative|_|) (number) = if number < 0 then Some(Negative) else None
与
let (|Zero|Positive|Negative|) (number) = if number = 0 then Zero elif number > 0 then Positive else Negative
在这种情况下,对我来说似乎毫无意义,因为我几乎回到了原始代码。 它将有它的用途。
活动模式是一个有趣的结构,尤其是在多次使用活动模式时。
翻译自: https://www.javacodegeeks.com/2019/01/active-pattern.html