Recursion with batteries that are not upside down - is this possible?

I played with Haskell recently and I came up with this function to find n th prime:

nthPrime 1 = 2 nthPrime 2 = 3 nthPrime n = aux [2, 3] 3 5 n where aux knownPrimes currentNth suspect soughtNth = let currentIsPrime = foldl (\ln -> l && suspect `mod` n /= 0) True knownPrimes in case (currentIsPrime, soughtNth == currentNth) of (True, True) -> suspect (True, False) -> aux (suspect:knownPrimes) (currentNth + 1) (suspect + 2) soughtNth _ -> aux knownPrimes currentNth (suspect + 2) soughtNth 

My question is, is there a way to have a cumulative parameter (in this case knownPrimes ) that does not reverse (how does this happen during transmission (suspect:knownPrimes) )?

I tried using knownPrimes ++ [suspect] , but this is also inefficient.

My hope is that if I can pass the known primes in order, then I can cut back on some of the strength checks a bit more.

+5
source share
1 answer

In Haskell, if you use a drive to create a list, but ultimately need to cancel it, it often happens that it is better to reset the drive and instead create a list lazily as a result of your calculation.

If you apply such thinking to the search for primes and take full advantage of laziness, you get the well-known technique of creating an endless list of all primes. If we reorganize your code as little as possible to use this technique, we will get something like:

 allPrimes = [2, 3] ++ aux 5 where aux suspect = let currentIsPrime = foldl (\ln -> l && suspect `mod` n /= 0) True $ takeWhile (\n -> n*n <= suspect) allPrimes in case currentIsPrime of True -> suspect : aux (suspect + 2) False -> aux (suspect + 2) nthPrime n = allPrimes !! (n-1) 

Now I deleted the unnecessary parameters and changed the code to accumulate in lazy production and used my own result as a source of simple divisors for testing (this is called "node binding"). Other than that, the only change is to add a takeWhile check: since the list we are testing, the divisors from is defined in terms of itself and is infinite to load, we need to know where in the list to stop the divisors checking, so we don’t get truly infinite recursion.

In addition, there is inefficiency in this code:

 foldl (\ln -> l && suspect `mod` n /= 0) True 

It is not a good way to check if there are any divisors in the list, because, as it is written, it does not stop as soon as the divisor is found, even if && cuts itself (stops as soon as its first argument turns out to be False ).

To ensure the correct layout, instead of foldr you can use

 foldr (\nr -> suspect `mod` n /= 0 && r) True 

Or, even better, use the predefined function all :

 all (\n -> suspect `mod` n /= 0) 

Using my comments

Here's what it would look like if you use all and reorganize it a bit:

 allPrimes :: [Integer] allPrimes = 2 : 3 : aux 5 where aux suspect | currentIsPrime = suspect : nextPrimes | otherwise = nextPrimes where currentIsPrime = all (\n -> suspect `mod` n /= 0) $ takeWhile (\n -> n*n <= suspect) allPrimes nextPrimes = aux (suspect + 2) nthPrime :: Int -> Integer nthPrime n = allPrimes !! (n-1) 
+7
source

Source: https://habr.com/ru/post/1205657/


All Articles