Насмешливо Ф# тестер делением


Я хотел бы задать 2 вопроса о модуляризации Ф файл#. Код-это просто рабочий пример простые множители, но это не главная тема. У меня есть главный файл (PrimeFactors.ФС), а другой файл для своего глумились версия (PrimeFactorsMock.FS) и тестовый файл (PrimeFactorsTests.ФС):

PrimeFactors.ПС

namespace PrimeFactors

module PrimeFactors = 

    type IPrimeChecker =
        abstract member IsPrime : int64 -> int64 -> bool        

    type PrimeFactors() = 
        interface IPrimeChecker with
            member this.IsPrime number i =
                number % i = 0L

        member this.CheckPrimes number i acc =
            match i with
                | _ when i > (number |> float |> sqrt |> int64)   -> number::acc
                | _ when (this :> IPrimeChecker).IsPrime number i -> this.CheckPrimes (number / i) i (i::acc)
                | _                                               -> this.CheckPrimes number (i + 1L) acc

        member this.Of number =
            if number = 1L then
                [||]
            else
                this.CheckPrimes number 2L [] |> List.toArray

PrimeFactorsMock.ПС

namespace PrimeFactors
open PrimeFactors

module PrimeFactorsMock =
    type PrimeFactorsMock() =
        inherit PrimeFactors() 

        let mutable stepCount = 0L

        interface IPrimeChecker with
          member this.IsPrime number i =
            stepCount <- stepCount + 1L
            number % i = 0L

        member this.StepCount number =
            stepCount <- 0L
            this.Of number |> ignore
            stepCount

PrimeFactorsTests.ПС

namespace PrimeFactors

open PrimeFactors
open NUnit.Framework
open FsUnit
open PrimeFactorsMock

[<TestFixture>]
module PrimeFactorTests =

    [<TestCase(1L, [||])>]
    [<TestCase(2L, [|2L|])>]
    [<TestCase(3L, [|3L|])>]
    [<TestCase(4L, [|2L; 2L|])>]
    [<TestCase(6L, [|3L; 2L|])>]
    [<TestCase(8L, [|2L; 2L; 2L|])>]
    [<TestCase(16L, [|2L; 2L; 2L; 2L|])>]
    [<TestCase(773L, [|773L|])>]
    [<TestCase(8L, [|2L; 2L; 2L|])>]
    let prime_factors_of_number number expect =
        PrimeFactors().Of number |> should equal expect

    [<TestCase(1L, 0L)>]
    [<TestCase(4L, 1L)>]
    [<TestCase(773L, 26L)>]
    let step_count_of_number number stepCount =
        PrimeFactorsMock().StepCount number |> should equal stepCount

Что мне нравится в том, что решение это пробный вариант отделена правильно с самого начального класса. Так что я только должны пересмотреть IPrimeChecker.IsPrimeне this.CheckPrimesи возможности все внутренние функции. Так что я сделал IsPrimeи только IsPrimeвиртуальные. Это бедный человек издевается над.

Мой вопрос об издевательском с объектом выражения , если это может быть достигнуто без создания PrimeFactorsMock.FS и меньше кодирования. Если мы переосмысливаем всю PrimeFactorsMock в тестовый файл, это не считается (по крайней мере, сделать его меньше). Цель состоит в том, чтобы сделать IsPrime функции заменимы в тестовый файл без особых хлопот.

Другой вопрос я хотел бы использовать активные шаблоны для IsPrime. Так что я могу использовать поиск по шаблону с (|Prime|NonPrime|) в this.CheckPrimes часть. Ограничение заключается в том, что нужно быть таким же, как заменимых, как уже реализованные виртуальные IsPrime.



178
2
задан 7 марта 2018 в 04:03 Источник Поделиться
Комментарии
1 ответ

Первое, что я заметил, это несколько вводит в заблуждение имя метода IsPrime. Я бы назвал это IsFactorпотому что он только проверяет, если я-это фактор номер - нет, если это является главным фактором.


Во-вторых, я думаю, что это проблема, что PrimeFactorsMock.IsPrime есть своя реализация number % i = 0L потому что вы тогда не оценивать реализацию базового класса, которая может оказаться недействительной.

Решение этой проблемы может быть, как показано ниже. Помните, что я бросил IPrimeChecker, потому что это лишнее.

module PrimeFactors = 

type PrimeFactors() =

// I've renamed IsPrime to IsFactor because it is only checking if i is a factor of number not if it is a prime factor
// You have to tuple the arguments to make it possible to be called from a derived class by base.IsFactor
abstract member IsFactor: uint64 * uint64 -> bool
default this.IsFactor (number, i) = number % i = 0UL

member this.CheckPrimes number i acc =
match i with
| _ when i > (number |> float |> sqrt |> uint64) -> number::acc
| _ when this.IsFactor (number, i) -> this.CheckPrimes (number / i) i (i::acc)
| _ -> this.CheckPrimes number (i + 1UL) acc

member this.Of number =
if number = 1UL then
[||]
else
this.CheckPrimes number 2UL [] |> List.toArray

module PrimeFactorsMock =
open PrimeFactors

type PrimeFactorsMock() =
inherit PrimeFactors()

let mutable stepCount = 0L

override this.IsFactor (number, i) =
stepCount <- stepCount + 1L
base.IsFactor (number, i) // Her the base class implementation is called

member this.StepCount number =
stepCount <- 0L
this.Of number |> ignore
stepCount


Когда дело доходит до выражения Object как макет для тестирования, я не нашел правильный подход в этом случае, потому что вы не можете расширить оригинального за тип тебя "выводят" из. Например, ниже прямой решение-это не выход:

let mock = { 
new PrimeFactors.PrimeFactors()
with
member this.IsFactor (number, i) =
stepCount <- stepCount + 1L
base.IsFactor (number, i)

// This is not allowed
let mutable stepCount = 0L
member this.StepCount number =
stepCount <- 0L
this.Of number |> ignore
stepCount

}


По данным активных шаблонов для Prime|NoPrime (или это Factor?) Я хотел бы использовать тип союза вместо определены следующим образом:

type FactorType =
| Factors of uint64 * uint64
| NonFactor

- использовать в таком виде, как сейчас:

let getFactorType a b = 
match a % b with
| r when r = 0UL -> Factors(b, a / b)
| _ -> NonFactor

- и как сочетается это:

match getFactorType num i with
| Factors(a, b) -> getPrimes b a (a::acc)
| NonFactor -> getPrimes num (i + 1UL) acc


Все решение, которые использует размеченные типа Союз-фактор/нефакторный и минимизировать глумиться основе, может быть в следующем - не утверждая, что это лучшая практика или что-то подобное.

module PF = 

// Instead of an active pattern, I use this discriminated union type
type FactorType =
| Factors of uint64 * uint64
| NonFactor

// Function prototype for functions testing if a number is a factor of another
// I've changed the domain type from int64 to uint64
type FactorChecker = uint64 * uint64 -> FactorType

type PrimeFactors() =

// This function is made static because it has nothing to do with the class itself
// and because I want to reuse it in the derived Mock class (se below)
// I renamed it to getFactorType from IsPrime,
// because IsPrime is somewhat misleading
// as it only checks if b is a factor of a not if it is a prime factor.
static member getFactorType (a, b) =
match a % b with
| r when r = 0UL -> Factors(b, a / b)
| _ -> NonFactor

// This could be static as well but here I made it a member.
// It would be nice to declare it protected, but that's not an option in F#
// In order to let the client provide the function to check for factors, the last argument is a function of type FactorChecker
member this.getFactors number (factorChecker: FactorChecker) =
if number < 2UL then
[]
else
// The stop condition is the (uint64 sqrt) of n
let max n = n |> float |> sqrt |> uint64

let rec getPrimes num i acc =
match i with
| _ when i > max num -> num::acc
| _ ->
match factorChecker (num, i) with
| Factors(a, b) -> getPrimes b a (a::acc)
| NonFactor -> getPrimes num (i + 1UL) acc

getPrimes number 2UL []

member this.Of (number: uint64) : uint64 list = this.getFactors number PrimeFactors.getFactorType

module PFMocks =
open PF

// This is a simplified version of PrimeFactorsMock without the IsPrime override
type PrimeFactorsMock() =
inherit PrimeFactors()

member this.StepCount number =
let mutable stepCount = 0L
let facChecker (a, b) =
stepCount <- stepCount + 1L
PrimeFactors.getFactorType (a, b)
this.getFactors number facChecker |> ignore
stepCount

1
ответ дан 10 марта 2018 в 10:03 Источник Поделиться