【问题标题】:QuickCheck Haskell - generating random lottery ticketsQuickCheck Haskell - 生成随机彩票
【发布时间】:2018-08-26 04:15:22
【问题描述】:

这行得通:

genAnimal :: Gen String
genAnimal = do
  animals <- shuffle ["tiger","rabbit","dragon","snake","rat","ox","pig","sheep","horse","monkey","dog"]
  return (head animals)

genWinner :: Gen String
genWinner = do 
  animal <- genAnimal
  prize <- choose (10::Int,1000::Int)
  return (unwords (replicate 3 animal) ++ " " ++ show prize)

genTicket :: Gen String
genTicket = do 
  animals <- replicateM 3 genAnimal
  prize <- choose (10::Int,1000::Int)
  return (unwords animals ++ " " ++ show prize)

genTickets :: Gen [String]
genTickets = do
  tickets <- replicateM 6 (oneof [genWinner, genTicket])
  return tickets

但它看起来很笨拙,有没有更明智的方法来组合这些生成器?它基本上随机挑选三只动物,然后随机抽取一个奖品,然后制作六张门票。

【问题讨论】:

  • 我觉得很奇怪票是String。拥有(静态)类型系统的想法是它保证某些合同。通过引入Ticket 数据类型,您可以约定结果是Ticket,而不仅仅是随机 字符串。
  • 我不清楚这些生成器应该做什么。 AFAICT、genWinnergenTicket 具有相同的行为。你能详细说明一下目标吗?
  • genWinner 生成三个相同的动物,genTicket 随机选择三个可能相同或不同的动物。

标签: haskell quickcheck


【解决方案1】:

正如@AJFarmar 所指出的,您可以使用elements 组合器从列表中选择一个随机元素,而不是采用随机版本的头部:

genAnimal :: Gen String
genAnimal = elements ["tiger","rabbit","dragon","snake","rat","ox",
                      "pig","sheep","horse","monkey","dog"]

此外,应用风格通常更简洁,尤其是当您为合并结果的纯计算引入辅助函数时:

genPrize :: Gen Int
genPrize = choose (10,1000)

genTicket :: Gen String
genTicket = ticket <$> replicateM 3 genAnimal <*> genPrize
  where ticket animals prize = unwords animals ++ " " ++ show prize

其他一些其他改进可能来自:

  • 改变您对生成器的看法,以生成您在末尾组合的单词,而不是必须为 unworded 并用空格粘合在一起的字符串;
  • 或者更好的是,引入一些数据类型来更好地模拟您的问题,并提供一个实用函数,仅在需要时以适当的String 形式打印它;
  • 摆脱使所有内容混乱的 gen 前缀
  • replicateM 使用vectorOf 别名,这在这种情况下更具可读性。

这可能会导致:

{-# OPTIONS_GHC -Wall #-}

module Lottery where

import Test.QuickCheck

type Animal = String
data Ticket = Ticket [Animal] Int

pticket :: Ticket -> String
pticket (Ticket ts prz) = unwords ts ++ ' ':show prz

tickets :: Gen [Ticket]
tickets = vectorOf 6 (oneof [winner, loser])
  where winner = Ticket <$> (replicate 3 <$> animal) <*> prize
        loser  = Ticket <$> (vectorOf 3 animal)      <*> prize
        animal = elements ["tiger","rabbit","dragon","snake","rat","ox",
                           "pig","sheep","horse","monkey","dog"]
        prize  = choose (10,1000)

main :: IO ()
main = print . map pticket =<< generate tickets

我觉得挺好看的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    相关资源
    最近更新 更多