Inserting a line at the beginning of the Cond in your .math.isPrimes will let it take the vector argument you give it:
0<type x; .z.s each x;
Your 6K strategy is to run through every 6th number and test one before and one behind. Since youre looking (only) for the 10,001st prime, you should be able to save space by keeping in your state the last pair tested, the result, and the count of primes seen so far we dont need a list of 10,000 primes.
nprimes4:{[N] / Nth prime is:(“i”$1,count::;.math.isPrime;::)@:2 3; / initial state: k:1; 2 3 found step:{[x;y;z] b:.math.isPrime n:-1 1+6*first x; / test 2 candidates (x+1,sum b;b;n) / new state }.; fs:{x>y . 0 1}[N;] step/is; / final state {[n;kc;b;p] (p where b)@(kc 1)-n}[N;;;] . fs }
Either the time we saved by not appending to the list of primes 5000+ times was lost to extra code or the interpreter was smart enough to do the appending without 5000+ internal copies.
Its instructive to compare with space-hungry Eratosthenes, which instead of arithmetic uses long vector ANDs.
np:{[N] / Nth prime es:{ / Eratosthenes’ sieve is:{(1#2;0b,1_x#10b)}; / initial state step:{(x,n;y&count[y]#i<>til n:1+i:y?1b)}. ; / step: sieve next prime {x>last first y}[floor sqrt x] step/is x }; rslv:raze @[;1;1+where@]::; / resolve result pt:rslv es::; / primes to pi:{x%log x}; / ?(x) first approximation @[;n]pt (N>pi@)(2*)/1000 }
Not intuitively obvious which will run faster. But it turns out vector-hungry q loves long vectors and implicit iteration.