Scala 的 "partial functions"' 概念和 F# 中的“.orElse”方法
Scala's notion of "partial functions"' & the ".orElse" method in F#
在 Scala 中有一个 "partial function" 的概念,它与 F# 的 function
关键字允许我实现的非常相似。然而,Scala 的部分函数也允许通过 orElse
方法进行组合,如下所示:
def intMatcher: PartialFunction[Any,String] = {
case _ : Int => "Int"
}
def stringMatcher: PartialFunction[Any,String] = {
case _: String => "String"
}
def defaultMatcher: PartialFunction[Any,String] = {
case _ => "other"
}
val msgHandler =
intMatcher
.orElse(stringMatcher)
.orElse(defaultMatcher)
msgHandler(5) // yields res0: String = "Int"
我想知道是否有办法在 F# 中实现相同的组合功能。
我可能会在这里使用部分活动模式,这样您就可以使用模式匹配。 Some(T) 匹配,None 不匹配。
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let (|Float|_|) (str: string) =
let mutable floatvalue = 0.0
if System.Double.TryParse(str, &floatvalue) then Some(floatvalue)
else None
let parseNumeric str =
match str with
| Integer i -> "integer"
| Float f -> "float"
| _ -> "other"
https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns
但值得注意的是,在您提供的这种人为设计的情况下,可以只使用一个匹配语句。我假设您的目标是拆分比赛条件。
let msgHandler (x: obj) =
match x with
| :? int -> "integer"
| :? float -> "float"
| _ -> "other"
我想出了两个解决方案来实现我的确切目标。一种是通过使用活动模式:
let orElse(fallback: 'a -> (unit -> 'b) option) (matcher: 'a -> (unit -> 'b) option) (arg: 'a) : (unit -> 'b) option =
let first = matcher(arg)
match first with
| Some(_) -> first
| None -> fallback(arg)
let (|StringCaseHandler|_|)(arg: obj) =
match arg with
| :? string -> Some(fun () -> "string")
| _ -> None
let (|IntCaseHandler|_|)(arg: obj) =
match arg with
| :? int -> Some(fun () -> "integer")
| _ -> None
let (|DefaultCaseHandler|_|)(arg: 'a) =
Some(fun () -> "other")
let msgHandler =
``|StringCaseHandler|_|`` |>
orElse ``|IntCaseHandler|_|`` |>
orElse ``|DefaultCaseHandler|_|``
具有活动模式的解决方案是安全的,因为在没有正确匹配的情况下它不会抛出 MatchFailureException
;而是返回 None
。
第二个涉及为 'a -> 'b
类型的函数定义一个扩展方法,它也尽可能接近 Scala 的 "partial function" orElse
行为,如果结果函数没有产生正确的匹配:
[<Extension>]
type FunctionExtension() =
[<Extension>]
static member inline OrElse(self:'a -> 'b,fallback: 'a -> 'b) : 'a -> 'b =
fun arg ->
try
self(arg)
with
| :? MatchFailureException -> fallback(arg)
let intMatcher : obj -> string = function
| :? int -> "integer"
let stringMatcher : obj -> string = function
| :? string -> "string"
let defaultMatcher : obj -> string = function
| _ -> "other"
let msgHandler: obj -> string = intMatcher
.OrElse(stringMatcher)
.OrElse(defaultMatcher)
你在Scala中的写法等同于在C#中使用扩展方法。它不是函数式编程特别惯用的。要在 F# 中严格使用可组合函数,您可以这样做。
// reusable functions
let unmatched input = Choice1Of2 input
let orElse f =
function
| Choice1Of2 input -> f input
| Choice2Of2 output -> Choice2Of2 output
let withDefault value =
function
| Choice1Of2 _ -> value
| Choice2Of2 output -> output
// problem-specific functions
let matcher isMatch value x =
if isMatch x then Choice2Of2 value
else Choice1Of2 x
let isInt (o : obj) = o :? int
let isString (o : obj) = o :? string
let intMatcher o = matcher isInt "Int" o
let stringMatcher o = matcher isString "String" o
// composed function
let msgHandler o =
unmatched o
|> orElse intMatcher
|> orElse stringMatcher
|> withDefault "other"
这里,Choice1Of2
表示我们还没有找到匹配项,包含不匹配的输入。 Choice2of2
表示我们找到了匹配项并包含输出值。
在 Scala 中有一个 "partial function" 的概念,它与 F# 的 function
关键字允许我实现的非常相似。然而,Scala 的部分函数也允许通过 orElse
方法进行组合,如下所示:
def intMatcher: PartialFunction[Any,String] = {
case _ : Int => "Int"
}
def stringMatcher: PartialFunction[Any,String] = {
case _: String => "String"
}
def defaultMatcher: PartialFunction[Any,String] = {
case _ => "other"
}
val msgHandler =
intMatcher
.orElse(stringMatcher)
.orElse(defaultMatcher)
msgHandler(5) // yields res0: String = "Int"
我想知道是否有办法在 F# 中实现相同的组合功能。
我可能会在这里使用部分活动模式,这样您就可以使用模式匹配。 Some(T) 匹配,None 不匹配。
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let (|Float|_|) (str: string) =
let mutable floatvalue = 0.0
if System.Double.TryParse(str, &floatvalue) then Some(floatvalue)
else None
let parseNumeric str =
match str with
| Integer i -> "integer"
| Float f -> "float"
| _ -> "other"
https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns
但值得注意的是,在您提供的这种人为设计的情况下,可以只使用一个匹配语句。我假设您的目标是拆分比赛条件。
let msgHandler (x: obj) =
match x with
| :? int -> "integer"
| :? float -> "float"
| _ -> "other"
我想出了两个解决方案来实现我的确切目标。一种是通过使用活动模式:
let orElse(fallback: 'a -> (unit -> 'b) option) (matcher: 'a -> (unit -> 'b) option) (arg: 'a) : (unit -> 'b) option =
let first = matcher(arg)
match first with
| Some(_) -> first
| None -> fallback(arg)
let (|StringCaseHandler|_|)(arg: obj) =
match arg with
| :? string -> Some(fun () -> "string")
| _ -> None
let (|IntCaseHandler|_|)(arg: obj) =
match arg with
| :? int -> Some(fun () -> "integer")
| _ -> None
let (|DefaultCaseHandler|_|)(arg: 'a) =
Some(fun () -> "other")
let msgHandler =
``|StringCaseHandler|_|`` |>
orElse ``|IntCaseHandler|_|`` |>
orElse ``|DefaultCaseHandler|_|``
具有活动模式的解决方案是安全的,因为在没有正确匹配的情况下它不会抛出 MatchFailureException
;而是返回 None
。
第二个涉及为 'a -> 'b
类型的函数定义一个扩展方法,它也尽可能接近 Scala 的 "partial function" orElse
行为,如果结果函数没有产生正确的匹配:
[<Extension>]
type FunctionExtension() =
[<Extension>]
static member inline OrElse(self:'a -> 'b,fallback: 'a -> 'b) : 'a -> 'b =
fun arg ->
try
self(arg)
with
| :? MatchFailureException -> fallback(arg)
let intMatcher : obj -> string = function
| :? int -> "integer"
let stringMatcher : obj -> string = function
| :? string -> "string"
let defaultMatcher : obj -> string = function
| _ -> "other"
let msgHandler: obj -> string = intMatcher
.OrElse(stringMatcher)
.OrElse(defaultMatcher)
你在Scala中的写法等同于在C#中使用扩展方法。它不是函数式编程特别惯用的。要在 F# 中严格使用可组合函数,您可以这样做。
// reusable functions
let unmatched input = Choice1Of2 input
let orElse f =
function
| Choice1Of2 input -> f input
| Choice2Of2 output -> Choice2Of2 output
let withDefault value =
function
| Choice1Of2 _ -> value
| Choice2Of2 output -> output
// problem-specific functions
let matcher isMatch value x =
if isMatch x then Choice2Of2 value
else Choice1Of2 x
let isInt (o : obj) = o :? int
let isString (o : obj) = o :? string
let intMatcher o = matcher isInt "Int" o
let stringMatcher o = matcher isString "String" o
// composed function
let msgHandler o =
unmatched o
|> orElse intMatcher
|> orElse stringMatcher
|> withDefault "other"
这里,Choice1Of2
表示我们还没有找到匹配项,包含不匹配的输入。 Choice2of2
表示我们找到了匹配项并包含输出值。