TimeOfDay 的任意实例
Arbitrary instance for TimeOfDay
使用 QuickCheck,我想创建一系列伪随机 TimeOfDay
值。
很容易创建特定的 TimeOfDay
:
now = TimeOfDay 17 35 22
使用 GHCi 8.6.5 打印此结果:
17:35:22
我认为使用 QuickCheck 创建 TimeOfDay
值所需的 Arbitrary
实例是:
instance Arbitrary TimeOfDay where
arbitrary = do
hour <- elements [0 .. 23]
min <- elements [0 .. 59]
-- Going till 60 accounts for leap seconds
sec <- elements [0 .. 60]
return $ TimeOfDay hour min sec
尽管此类型检查,运行 以下行挂起 GHCi 并在几秒钟后将 Killed
写入控制台:
sample (arbitrary :: Gen TimeOfDay)
错误在哪里?
错误的原因是 TimeOfDay
的最后一个构造函数参数是具有 picosecond 分辨率的秒数。
我假设因此,sec <- elements [0 .. 60]
不是生成 61 个值之一,而是生成 61 * 10^2 个值之一。
这解决了问题:
instance Arbitrary TimeOfDay where
arbitrary = do
hour <- elements [0 .. 23]
min <- elements [0 .. 59]
-- Going till 60 accounts for leap seconds
picoSecond <- elements [1, 2 .. 60]
return $ TimeOfDay hour min picoSecond
现在,sample (arbitrary :: Gen TimeOfDay)
生成例如:
17:19:03
15:49:58
01:28:40
13:20:07
12:00:01
12:35:45
13:25:33
12:55:20
07:11:54
21:10:46
14:34:15
如果您还需要亚秒范围内的值,请更改列表生成器的步长:
picoSecond <- elements [1, 1.1 .. 60]
示例值:
04:09:23.6
10:22:50.4
18:56:57.6
07:12:07.8
08:12:02.6
14:20:40
05:42:21.4
14:35:42.3
02:47:32.6
22:02:26.2
08:26:09.4
如您所见,todSeconds
的类型为 Pico
,它是一个定点数,分辨率为 10-12,所以这意味着 [0 .. 60]
有 6×1013+1 值。这很容易需要 ~1000 秒来遍历整个列表。
话虽如此,您一开始就不需要在这里使用 elements
。我们可以使用 choose :: Random a => (a, a) -> Gen a
来生成一个范围内的随机值(包括两个范围)。
然后我们可以将 Arbitrary
定义为:
instance Arbitrary TimeOfDay where
arbitrary = TimeOfDay
<$> choose (0, 23)
<*> choose (0, 59)
<*> (fmap MkFixed (choose (0, 61*10^12-1)))
然后我们得到:
Main> sample (arbitrary :: Gen TimeOfDay)
15:45:04.132804129488
11:06:12.447614162981
12:07:50.773642440667
04:40:47.966398431784
02:30:09.60931551059
00:51:46.564756092467
07:57:44.170698241052
02:45:57.743854623407
00:17:22.627238967351
13:03:57.364852826473
11:12:34.894890974241
如果你不想要这些皮秒,我们可以在fmap
中做乘法:
instance Arbitrary TimeOfDay where
arbitrary = TimeOfDay
<$> choose (0, 23)
<*> choose (0, 59)
<*> (fmap (MkFixed . (10^12 *)) (choose (0, 60)))
然后我们得到:
Main> sample (arbitrary :: Gen TimeOfDay)
15:00:53
14:02:44
14:44:40
12:40:12
09:55:39
10:06:02
15:00:51
15:52:23
16:59:05
22:38:45
20:23:15
使用 QuickCheck,我想创建一系列伪随机 TimeOfDay
值。
很容易创建特定的 TimeOfDay
:
now = TimeOfDay 17 35 22
使用 GHCi 8.6.5 打印此结果:
17:35:22
我认为使用 QuickCheck 创建 TimeOfDay
值所需的 Arbitrary
实例是:
instance Arbitrary TimeOfDay where
arbitrary = do
hour <- elements [0 .. 23]
min <- elements [0 .. 59]
-- Going till 60 accounts for leap seconds
sec <- elements [0 .. 60]
return $ TimeOfDay hour min sec
尽管此类型检查,运行 以下行挂起 GHCi 并在几秒钟后将 Killed
写入控制台:
sample (arbitrary :: Gen TimeOfDay)
错误在哪里?
错误的原因是 TimeOfDay
的最后一个构造函数参数是具有 picosecond 分辨率的秒数。
我假设因此,sec <- elements [0 .. 60]
不是生成 61 个值之一,而是生成 61 * 10^2 个值之一。
这解决了问题:
instance Arbitrary TimeOfDay where
arbitrary = do
hour <- elements [0 .. 23]
min <- elements [0 .. 59]
-- Going till 60 accounts for leap seconds
picoSecond <- elements [1, 2 .. 60]
return $ TimeOfDay hour min picoSecond
现在,sample (arbitrary :: Gen TimeOfDay)
生成例如:
17:19:03
15:49:58
01:28:40
13:20:07
12:00:01
12:35:45
13:25:33
12:55:20
07:11:54
21:10:46
14:34:15
如果您还需要亚秒范围内的值,请更改列表生成器的步长:
picoSecond <- elements [1, 1.1 .. 60]
示例值:
04:09:23.6
10:22:50.4
18:56:57.6
07:12:07.8
08:12:02.6
14:20:40
05:42:21.4
14:35:42.3
02:47:32.6
22:02:26.2
08:26:09.4
如您所见,todSeconds
的类型为 Pico
,它是一个定点数,分辨率为 10-12,所以这意味着 [0 .. 60]
有 6×1013+1 值。这很容易需要 ~1000 秒来遍历整个列表。
话虽如此,您一开始就不需要在这里使用 elements
。我们可以使用 choose :: Random a => (a, a) -> Gen a
来生成一个范围内的随机值(包括两个范围)。
然后我们可以将 Arbitrary
定义为:
instance Arbitrary TimeOfDay where
arbitrary = TimeOfDay
<$> choose (0, 23)
<*> choose (0, 59)
<*> (fmap MkFixed (choose (0, 61*10^12-1)))
然后我们得到:
Main> sample (arbitrary :: Gen TimeOfDay)
15:45:04.132804129488
11:06:12.447614162981
12:07:50.773642440667
04:40:47.966398431784
02:30:09.60931551059
00:51:46.564756092467
07:57:44.170698241052
02:45:57.743854623407
00:17:22.627238967351
13:03:57.364852826473
11:12:34.894890974241
如果你不想要这些皮秒,我们可以在fmap
中做乘法:
instance Arbitrary TimeOfDay where
arbitrary = TimeOfDay
<$> choose (0, 23)
<*> choose (0, 59)
<*> (fmap (MkFixed . (10^12 *)) (choose (0, 60)))
然后我们得到:
Main> sample (arbitrary :: Gen TimeOfDay)
15:00:53
14:02:44
14:44:40
12:40:12
09:55:39
10:06:02
15:00:51
15:52:23
16:59:05
22:38:45
20:23:15