1

I want to sample from an infinite list of floats for QuickCheck consumption. However, as I intend to use division, I want to remove zero from that list. It is such a conceptually simple problem I was wondering if I could do it with a list comprehension, and if not, which would be the simplest way to achieve this in Haskell?

[x | x <- floats, x /= 0] -- this seems reasonable, but where do I get floats from?

My current workaround (yuck):

import Test.QuickTest

divGen :: Gen (Maybe Float)
divGen = do
    x <- arbitrary
    if x /= 0
    then return $ Just x
    else return Nothing
dmvianna
  • 15,088
  • 18
  • 77
  • 106
  • 3
    "Where do I get `floats` from?" is quite a question; I don't see that it can be answered from the information given. What do you want to be in that list...? – Daniel Wagner Feb 16 '16 at 06:12
  • @DanielWagner, check my edit. That workaround works and gives me the kind of values I expect. – dmvianna Feb 16 '16 at 06:14
  • It seems your actual question is how to generate an infinite list of random floats? The list comprehension in the question is perfectly fine for filtering zero elements out of an infinite list of floats. – Ben Feb 16 '16 at 06:49

4 Answers4

7

You can generate arbitrary values that satisfy a given predicate using the suchThat combinator of QuickCheck:

divisor :: Gen Float
divisor = arbitrary `suchThat` (/= 0)

Example usage:

my_prop x = forAll divisor $ \d -> (x / d) * d =~= x
Cactus
  • 27,075
  • 9
  • 69
  • 149
  • It is a hard choice between yours and @Zeta's answer. Both improved my knowledge of QuickCheck. Thanks! – dmvianna Feb 16 '16 at 21:43
  • Where does the `=~=` operator come from? – The Hoff Dec 08 '20 at 21:51
  • `=~=` here is meant to be a sufficiently inexact comparator (since comparing `Double`s exactly isn't a good idea). See e.g. https://hackage.haskell.org/package/elynx-tools-0.5.0/docs/ELynx-Tools.html#v:-61--126--61- – Cactus Dec 09 '20 at 06:25
5

That's already in QuickCheck, namely NonZero. Your infinite list of floats can be modeled as

nonZeroFloat :: Gen Float
nonZeroFloat = fmap getNonZero arbitrary

-- You probably want to use a shorter name:
infiniteListOfNonZeroFloats :: Gen [Float]
infiniteListOfNonZeroFloats = infiniteListOf nonZeroFloat

Afterwards, you can use forAll:

prop_something = forAll infiniteListOfNonZeroFloats $ \xs -> ...
-- or
prop_something = forAll (infinitelistOf $ getNonZero `fmap` arbitrary) $ \xs ->
                    ...

Note that the use of NonZero is a lot smoother with pattern matching:

prop_nonzero :: NonZero Float -> ...
prop_nonzero (NonZero x) = ...
Zeta
  • 103,620
  • 13
  • 194
  • 236
1

So if you intend only to remove zeros from a list then you can do the following:

  1. Function definition:

    withoutZeros:: (Eq a,Num a)->[a]->[a] 
    
  2. This code will drop all the zeros. You can drop any number just by changing the condtion inside the (/=).

    withoutZeros =filter (/=0)  
    

I used (Eq a,Num a) in my function definition because Eq a-> is required for the comparison (i.e. (/=)) and Num a->. This will let you use this function on both Integer and Float lists.

JMAA
  • 1,730
  • 13
  • 24
  • Welcome to Stackoverflow! Did you know you can use markdown-style formatting in your answers and preview them before you post? I've submitted an edit to your answer to make it more readable, but I'm not very familiar with Haskell, so feel free to fix any errors I may have accidentally introduced. – JMAA Oct 10 '18 at 20:25
0

Could I do it with a list comprehension?

Sure, that comprehension looks fine to me.

Where do I get floats from?

Depends what you want to be in it.

For your generator code, the only comment I would make is that rejection sampling is the usual trick:

divGen = do
    x <- arbitrary
    if x /= 0 then return x else divGen
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380