我正在使用QuickCheck生成任意函数,并且我想生成任意injective函数(即,当且仅当f a == f b
时才是a == b
。
我思想我想通了:
newtype Injective = Injective (Fun Word Char) deriving Show
instance Arbitrary Injective where
arbitrary = fmap Injective fun
where
fun :: Gen (Fun Word Char)
fun = do
a <- arbitrary
b <- arbitrary
arbitrary `suchThat` \(Fn f) ->
(f a /= f b) || (a == b)
但是我看到的情况是生成的函数将不同的输入映射到相同的输出。
我想要的是:
import qualified Data.Set as Set
newtype Injective = Injective ([Char] {- simply a list without duplicates -})
deriving Show
instance Arbitrary Injective where
arbitrary = Injective . lazyNub <$> arbitrary
lazyNub :: Ord a => [a] -> [a]
lazyNub = go Set.empty
where go _ [] = []
go forbidden (x:xs)
| x `Set.member` forbidden = go forbidden xs
| otherwise = x : go (Set.insert x forbidden) xs
这不是很有效,对于您的应用程序可能不太合适,但这可能是您可以做的最好的事情。
在实践中,要实际使用Injective
作为函数,您需要将值包装在只有O(log
n)查找时间的合适结构中。不幸的是,
Data.Map.Lazy
不够懒惰,您可能需要手工烘焙一系列指数增长的地图。