Как заметил @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
что, мне кажется, выглядит неплохо.