Простая функция Add для работы с целыми числами и числами с плавающей точкой - PullRequest
1 голос
/ 09 ноября 2019

Я довольно новичок в F #, я пытаюсь написать некоторый код, который не различает числа с плавающей точкой и целые числа, и рассматривает их оба просто как "числа". Для начала я хочу простую функцию «добавить», которая будет добавлять два числа, где каждое число может быть целым или с плавающей точкой.

Это мой текущий подход, который не выглядит элегантным, возможно, из-за моего незнания тонкостей F #

type Number =
       | Integer of int
       | Float of float

   let add (a: Number, b: Number) : Number =
       match a with
        | Integer(a) ->
              match b with
                  | Integer(b) -> Integer(a + b)
                  | Float(b) -> Float(double a + b)
        | Float(a) ->
              match b with
                  | Integer(b) -> Float(a + double b)
                  | Float(b) -> Float(a + b)

Это работает, но для такого кода достаточно многопростая функция. В частности, если бы я хотел также функцию «вычитания», мне пришлось бы скопировать / вставить всю функцию и просто изменить «+» на «-», что кажется действительно неэффективным. Есть ли лучший способ сделать это в F #?

Ответы [ 2 ]

2 голосов
/ 10 ноября 2019

Применение ключевого слова inline заставит F # использовать статически разрешенные параметры типа (SRTP) для автоматической генерации двух функций (одна для int и одна для float) во время компиляции.

let inline add a b = a + b

add 5 6

add 5.2  5.8

тип этой функции, как показано ниже. Обратите внимание, что каждый параметр, а также возвращаемое значение могут иметь различный тип (^ a, ^ b или ^ c). Символ ^ обозначает SRTP.

val inline add :
  a: ^a -> b: ^b ->  ^c
    when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)

Вы можете, если хотите, принудительно заставить оба параметра и возвращаемое значение иметь одинаковый тип.

let inline add (a: ^a) (b: ^a) : ^a = a + b

Этоподпись функции:

val inline add :
  a: ^a -> b: ^a ->  ^a when  ^a : (static member ( + ) :  ^a *  ^a ->  ^a)
2 голосов
/ 09 ноября 2019

Один из возможных подходов может быть таким:

// Generic function to reduce two numbers into a number
//  i reduces nested integers
//  f reduces nested floats
//  In order for i,f to be merged into single reducer
//  F# would have to support higher ranked types (I think)
//  but F# does not
let reduce i f (a: Number) (b: Number) : Number =
  match a, b with
  | Integer a, Integer b -> Integer (i a b)
  | Integer a, Float   b -> Float   (f (float a) b)
  | Float   a, Integer b -> Float   (f a (float b))
  | Float   a, Float   b -> Float   (f a b)

// Define common reducer functions
//  Operators +,-,* and / are parametric polymorphic
//  So the first ( + ) becomes an int -> int -> int
//  the second ( + ) becomes an float -> float -> float
let add       = reduce ( + ) ( + )
let subtract  = reduce ( - ) ( - )
let multiply  = reduce ( * ) ( * )
let divide    = reduce ( / ) ( / )
...