Понятие Скалы о "частичных функциях" и методе ".orElse" в F # - PullRequest
0 голосов
/ 17 декабря 2018

В Scala есть понятие «частичной функции», которое довольно похоже на то, чего позволяет достичь ключевое слово 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 #.

Ответы [ 3 ]

0 голосов
/ 17 декабря 2018

Я придумал два решения для достижения моей точной цели.Одним из них является использование активных шаблонов:

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 для "частичной функции" 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)
0 голосов
/ 17 декабря 2018

То, как вы написали это в 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 означает, что мы нашли совпадение и содержит выходное значение.

0 голосов
/ 17 декабря 2018

Я бы, вероятно, использовал бы здесь частичный активный образец, чтобы вы могли использовать сопоставление с образцом.Некоторые (T) совпадения, ни один не совпадает.

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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...