1

I have this function:

generatePrimes :: Integral a => a -> [a]
generatePrimes n = [i | i <- [2..], isPrime i]

I am trying to get the first n primes. I know that I can call the function in main by using the take function (and get the first n elements of the list), but I want to be able to have the function stop when it reaches n primes (inside the function), so that when it is called in main such as:

generatePrimes 8

it will display a list with only the first 8 primes.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
user3247014
  • 11
  • 1
  • 2
  • 5
    I don't understand what you are saying about "stopping inside the function". You could simply define `generatePrimes n = take n allPrimes`. Due to lazyness this will stop after finding `n` primes, there's no real overhead. The result *is* a list of the first `n` primes. Please clarify that requirement. – Bakuriu Mar 02 '16 at 18:05
  • 1
    `take` will 'stop' once it has pulled the desired number of elements from the list. Why doesn't that fit your requirements? – Mark Seemann Mar 02 '16 at 18:05
  • Explicitly testing each number for being prime isn't a very good way to generate a list of primes. For one thing, the tests on the even numbers after 2 are pointless. – John Coleman Mar 02 '16 at 18:11
  • 1
    Somewhat related, for those of you who haven't already read it, is this wonderful Functional Pearl: https://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf – Benjamin Hodgson Mar 02 '16 at 18:19

2 Answers2

5

The answer to your question as stated is to simply move take into the definition of generatePrimes, thus:

generatePrimes :: Integral a => Int -> [a]
generatePrimes n = take n [i | i <- [2..], isPrime i]

If it is important that you keep the exact same type signature as in the question, you may use the more polymorphic version of take available in Data.List:

import Data.List
generatePrimes :: Integral a => a -> [a]
generatePrimes n = genericTake n [i | i <- [2..], isPrime i]

(Indeed, this implementation has the even more general type generatePrimes :: (Integral a, Integral i) => i -> [a].)

However, this is anti-modular in the presence of lazy evaluation; you should leave control over how much of the list is consumed to the consumer rather than the producer.

Community
  • 1
  • 1
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • Couldn't match expected type `Int' with actual type `a' `a' is a rigid type variable bound by the type signature for generatePrimes :: Integral a => a -> [a] at Primes.hs:53:19 Relevant bindings include n :: a (bound at Primes.hs:54:16) generatePrimes :: a -> [a] (bound at Primes.hs:54:1) In the first argument of `take', namely `n' In the expression: take n [i | i <- [2 .. ], isPrime i] – user3247014 Mar 02 '16 at 18:16
  • @user3247014 Ah. You may use `Data.List.genericTake` instead of `take`, or change the type signature. I will update my answer shortly. – Daniel Wagner Mar 02 '16 at 18:17
0

Another implementation of printing the first n prime numbers can be done with pattern matching. Given as input a list that its range is from 2 to n, the function isPrime returns all the values that are prime numbers.

isPrime [p] = if null ([x | x <- [2.. p - 1], mod p x == 0])
              then show p 
              else ""

isPrime (p:ps) = if null ([x | x <- [2.. p - 1], mod p x == 0])
                 then show p ++ ", " ++ isPrime(ps)
                 else "" ++ isPrime(ps)

In order to execute the first n numbers, we can use ranges and just type: isPrime[2..n]. We consider number 2 to be the first prime number.