Undefined method `>' for nil:NilClass (NoMethodError)

I have a PHP script that calculates and counts the number of prime numbers in 1 to 1,000,000 correctly.
I am trying to convert the script into Ruby_2.7.0. on Ubuntu_20.04.``
The full script is at http://davekimble.net/primes.rb
Line 37: if n > sqrt(x) then break end
which the interpreter errors:

Traceback (most recent call last):
	4: from ./simple/primes/primes.rb:33:in `<main>'   
 
	3: from ./simple/primes/primes.rb:33:in `each'   

	2: from ./simple/primes/primes.rb:35:in `block in <main>'  
 
	1: from ./simple/primes/primes.rb:35:in `each'  

./simple/primes/primes.rb:37:in `block (2 levels) in <main>': undefined method `>' for nil:NilClass (NoMethodError)  

I don’t think “>” is a method, and I don’t think “n” is a nil:NilClass, so I can’t fix it.
What is going wrong ?

You did it wrong. The corrected code is:

#!/usr/bin/ruby
TIME = Time.now
# primes
# standard functions

# cast numeric to string
def str(a)
    return a.to_s
end

# a to the power of b
def pow(a, b)
    return a ** b
end

# square root of a
def sqrt(a)
    return Math.sqrt(a)
end

# modulo
def mod(a, b)
    return a % b
end

alias display print
alias input gets
require "date"
eol = "\n"

xstart = DateTime.now
primes = [2]
primecount = 1

for x in 3..1_000_000 do
    prime = true
    for y in 0..primecount do
        n = primes[y]
        if n > sqrt(x) then break end
        if mod(x, n) == 0 then prime = false end
    end
    if prime then
        primes[primecount] = x
        primecount = primecount+1
    end
end
xend = DateTime.now
display str(primecount)+" in "+str(xstart - xend)+" seconds"+eol

puts "\n\e[2K#{primecount} primes found in #{Time.now.-(TIME).round(4)}s"

Your code is terrible and slow. Fast brute force one:

Faster Eratosthenes:

TIME = Time.now
N = 1_000_000
PRIMES = (2..N).to_a.unshift(nil, nil)

i = -1
while (i += 1) < N
	next unless x = PRIMES[i]

	if (sqr = x ** 2) > N
		PRIMES.tap(&:compact!)
		break
	end

	j = sqr - x
	PRIMES[j] = nil while (j += x) <= N
end

puts "\n\e[2K#{PRIMES.count} primes found in #{Time.now.-(TIME).round(4)}s"

Benchmarks

10K

Your Code

1229 in -56702693/86400000000000 seconds

1229 primes found in 0.0582s

My Code

1229 primes found in 0.0013s

100K

Your Code

9592 in -322906859/21600000000000 seconds

9592 primes found in 1.293s

My Code

9592 primes found in 0.0147s

1M

Your Code

78498 primes found in 32.5214s

My Code

78498 primes found in 0.1598s

Thanks for your reply, but unfortunately you didn’t answer my question: what is wrong with “if n > sqrt(x) then break end” ? What did you do to it to fix it before benchmarking it ? I haven’t made it run yet.

As I understand it a NilClass is not an empty class, it is a non-existing class, and yet n seems to be a member of it.

“Your code is terrible and slow”

I agree it is slow, it has never even run, but “terrible” ? It was not written to be fast, but to be easy to understand and free of weird syntax. On that basis,

PRIMES.tap(&:compact!)

is not acceptable.

Also when I run your script it produces

./simple/primes/primes2.rb: line 1: TIME: command not found
./simple/primes/primes2.rb: line 2: N: command not found
./simple/primes/primes2.rb: line 3: syntax error near unexpected token `('
./simple/primes/primes2.rb: line 3: `PRIMES = (2..N).to_a.unshift(nil, nil)'

also your code needs a N-sized array, and doesn’t have any way of dealing with N = 1_000_000_000_000, while my code only needs a x sized array.

So it could be argued that it’s YOUR code that is terrible.
Obviously it is a matter of opinion.

Just had a visitor come up to my window

  • a sub-adult Southern Cassowary, presumably temporarily separated from his father and cautiously looking for food.

If I reduce the script further:

#!/usr/bin/ruby
def sqrt(a)
    return Math.sqrt(a)
end
x = 16
n = 1
for i in 0..10 do
    for j in 0..10 do
        if n > sqrt(x) then break end
    end
end

It works - which makes me look harder at my previous line, line 36, “n = primes[y]”.
This is the PHP way of doing it, the Ruby way is “n = primes.take(y)”.
Also line 42, “primes[] = x” is the PHP way of doing it, the Ruby way is “primes.push(x)” .

So fixing that up we have:

max = 1_000_000
primes = [2]
for x in 3..max do 
    prime = true
    for y in 0..primes.count do
        n = primes.take(y)
        if n > sqrt(x) then break end
        if mod(x, n) == 0 then prime = false end
    end
    if prime then 
    primes.push(x)
    end
end

Running that gives:

Traceback (most recent call last):
	4: from ./simple/primes/primes.rb:33:in `<main>'
	3: from ./simple/primes/primes.rb:33:in `each'
	2: from ./simple/primes/primes.rb:35:in `block in <main>'
	1: from ./simple/primes/primes.rb:35:in `each'
./simple/primes/primes.rb:38:in `block (2 levels) in <main>': undefined method `>' for []:Array (NoMethodError)

which leaves me puzzled.

Finally, there must be something wrong in my last post. It is ‘the Ruby way is “n = primes.take(y)”’. That should read 'the Ruby way is “n = primes.at(y)” ’
So fixing that up, it works. And the benchmarking against PHP:

For 1_000_000 on i7-8565:
PHP: 78498 in 4.2546830177307 seconds
Ruby: 78498 in 16.602168063 seconds

‘>’ is a method.
n is an array.

What are you trying to do ? Comparing and array vs a sqrt doesnt make sense.
Either you want to iterate over the array and compare individual values, or compare vs the last element etc or compare vs the length of the array etc ?

Sorry dont have much insight into this solution so not sure what you are trying to do.

'>' is a method.
n is an array.

If the interpreter had said it that clearly, I would have guessed what was going wrong. n was not supposed to have been an array but an element of an array, hence n = primes[y] was the problem. Changing that to n = primes.at[y] fixes it. All caused by inadvertently doing things in the PHP style, which is prettier I think. If the common methods were .get() and .put() it would be more intuitive. I can alias those.

What I was really after was some code which ran at 100% CPU time for several seconds without interruptions.

There are hundreds of methods for the array class, some of which are so specific to one problem, that they should be omitted.

I am afraid, maybe because you don’t know much about Ruby. If you are a noob, you will see such syntax most of the time in the near future.


This is a Ruby script and not a BASH script:

There’s no #!/usr/bin/env ruby in my code, so it runs in BASH when you make it executable and execute it. Prepend that line in the beginning to get it working. But anyway it’s getting off topic.