【问题标题】:How extract the int from a FsCheck.Gen.choose如何从 FsCheck.Gen.choose 中提取 int
【发布时间】:2015-04-17 16:13:53
【问题描述】:

我是 F# 新手,看不到如何从中提取 int 值:

let autoInc = FsCheck.Gen.choose(1,999)

编译器说类型是Gen<int>,但无法从中获取int!。我需要转成十进制,两种类型都不兼容。

【问题讨论】:

  • 你看过Gen module中的功能了吗?
  • 是的。但我不明白这段代码。代码说`让选择(l,h)= rand |> map(范围(l,h)>> fst)`但无法解释它的含义
  • 该表达式的结果不是数字,而是数字的生成器。它里面没有“包含”任何特定的数字,所以没有什么可以“提取”的。如果只想生成随机数,请使用System.Random
  • @mamcx :嗯,这是Gen.choose 的源代码,但我没有将您链接到源代码是有原因的——我将您链接到文档,你应该阅读。

标签: f# fscheck


【解决方案1】:

从消费者的角度来看,您可以使用Gen.sample 组合器,给定一个生成器(例如Gen.choose),它会返回一些示例值。

Gen.sample的签名是:

val sample : size:int -> n:int -> gn:Gen<'a> -> 'a list

(* `size` is the size of generated test data
   `n`    is the number of samples to be returned
   `gn`   is the generator (e.g. `Gen.choose` in this case) *)

您可以忽略size,因为Gen.choose 忽略它,因为它的分布是均匀的,并执行以下操作:

let result = Gen.choose(1,999) |> Gen.sample 0 1 |> Seq.exactlyOne |> decimal

(* 0 is the `size` (gets ignored by Gen.choose)
   1 is the number of samples to be returned *)

result 应该是闭区间 [1, 999] 中的值,例如897.

【讨论】:

    【解决方案2】:

    您好,补充一下 Nikos 已经告诉您的内容,您可以通过以下方式获得 1 到 999 之间的小数:

    #r "FsCheck.dll"
    
    open FsCheck
    
    let decimalBetween1and999 : Gen<decimal> =
        Arb.generate |> Gen.suchThat (fun d -> d >= 1.0m && d <= 999.0m)
    
    let sample () = 
        decimalBetween1and999
        |> Gen.sample 0 1 
        |> List.head 
    

    您现在只需使用sample () 即可获得随机小数。

    如果您只想要 1 到 999 之间的整数,但将它们转换为 decimal,您可以这样做:

    let decimalIntBetween1and999 : Gen<decimal> =
        Gen.choose (1,999)
        |> Gen.map decimal
    
    let sampleInt () = 
        decimalIntBetween1and999
        |> Gen.sample 0 1 
        |> List.head 
    

    你可能真正想要做什么

    使用它来为您编写一些不错的类型并检查这样的属性(这里使用 Xunit 作为测试框架和 FsCheck.Xunit 包:

    open FsCheck
    open FsCheck.Xunit
    
    type DecTo999 = DecTo999 of decimal
    
    type Generators = 
        static member DecTo999 =
            { new Arbitrary<DecTo999>() with
                override __.Generator = 
                    Arb.generate 
                    |> Gen.suchThat (fun d -> d >= 1.0m && d <= 999.0m)
                    |> Gen.map DecTo999
            }
    
    [<Arbitrary(typeof<Generators>)>]
    module Tests =
    
      type Marker = class end
    
      [<Property>]
      let ``example property`` (DecTo999 d) =
        d > 1.0m
    

    【讨论】:

    • 或通过管道输入Operators.decimal&lt;^T&gt;,例如.. |&gt; Gen.sample 0 1 |&gt; Seq.exactlyOne |&gt; decimal
    • 是的,有几个选项 - 选择你喜欢的任何一个
    【解决方案3】:

    Gen&lt;'a&gt; 是一种本质上抽象函数int -&gt; 'a 的类型(实际的类型有点复杂,但我们暂时忽略)。这个函数是纯函数,即当给定相同的 int 时,每次都会得到相同的 'a 实例。这个想法是 FsCheck 生成一堆随机整数,将它们提供给 Gen 函数,输出您感兴趣的 'a 类型的随机实例,并将它们提供给测试。

    所以你无法真正摆脱 int。你手中有一个函数,给定一个 int,生成另一个 int。

    Gen.sample 如另一个答案所述,本质上只是将一系列随机整数提供给函数并将其应用于每个整数,然后返回结果。

    这个函数是纯函数这一事实很重要,因为它保证了可重复性:如果 FsCheck 发现一个测试失败的值,您可以记录输入 Gen 函数的原始 int - 使用该种子重新运行测试保证生成相同的值,即重现错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-20
      • 2016-02-16
      • 1970-01-01
      • 2010-09-13
      • 1970-01-01
      • 2019-03-02
      • 1970-01-01
      • 2017-02-02
      相关资源
      最近更新 更多