QuickCheck Haskell - создание случайных лотерейных билетов - PullRequest
0 голосов
/ 26 августа 2018

Это работает:

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

Но это выглядит неуклюже, есть ли более разумный способ объединить эти генераторы? В основном он выбирает трех случайных животных, затем случайный приз и затем получает шесть билетов.

1 Ответ

0 голосов
/ 26 августа 2018

Как заметил @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

Несколько других дополнительных улучшений могут быть вызваны:

  • изменением вашей перспективына ваших генераторах, чтобы генерировать слова, которые вы объединяете в конце, вместо строк, которые должны быть unword отредактированы и склеены вместе с пробелами;
  • или еще лучше, введя некоторые типы данных, чтобы лучше смоделировать вашу проблему и обеспечитьутилита для печати в соответствующей форме String только при необходимости;
  • избавление от префиксов gen, которые загромождают все
  • , используя псевдоним vectorOf для replicateM который немного более читабелен в этом контексте.

Это может привести к чему-то вроде:

{-# 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

что, мне кажется, выглядит неплохо.

...