duckets615
duckets615

Reputation: 45

Refactor this method into a one liner

Simple code to find a perfect square

def perfect_square(num)
  (1..num).each {|n| return true if n*n == num }
  false    
end``

Is there a way to get the first line to return false instead of having to use a new line with just false below it?

Upvotes: 1

Views: 164

Answers (6)

Cary Swoveland
Cary Swoveland

Reputation: 110685

I thought it might be interesting to benchmark the various methods. Any surprises?

require 'benchmark'
require 'prime'

def duckets(num)
  (1..1+num/2).each {|n| return true if n*n == num }
  false    
end

def steenslag(num)
  Integer.sqrt(num)**2 == num
end

def iGian(num)
  num.prime_division.all? { |_, exp| exp % 2 == 0 }
end

def cary(num)
  (1..1+num/2).bsearch { |n| n*n >= num }**2 == num
end

def test(arr, meth)
  arr.map { |num| method(meth).call(num) }
end

This method maps each element of arr to true if it is a perfect square (else false), using method meth (a symbol).

def benchem(arr)
  Benchmark.bm do |x|
    x.report("enumerate")      { test(arr, :duckets)   }
    x.report("integer_sqrt")   { test(arr, :steenslag) }
    x.report("prime_divisors") { test(arr, :iGian)     }
    x.report("bsearch")        { test(arr, :cary)      }
  end
end

benchem([*2..100])
    user           system     total       real
  enumerate       0.000244   0.000000   0.000244 (  0.000229)  #3
  integer_sqrt    0.000049   0.000000   0.000049 (  0.000048)  #1
  prime_divisors  0.000963   0.000000   0.000963 (  0.000967)  #4
  bsearch         0.000086   0.000000   0.000086 (  0.000086)  #2

benchem([*101..1000])
    user           system     total       real
  enumerate       0.018636   0.000000   0.018636 (  0.018662)  #4
  integer_sqrt    0.000252   0.000000   0.000252 (  0.000255)  #1
  prime_divisors  0.003402   0.000000   0.003402 (  0.003407)  #3
  bsearch         0.000870   0.000000   0.000870 (  0.000872)  #2

benchem([*1001..10000])
    user           system     total       real
  enumerate       1.345501   0.000000   1.345501 (  1.345650)  #4 
  integer_sqrt    0.004647   0.000000   0.004647 (  0.004655)  #1
  prime_divisors  0.048204   0.000000   0.048204 (  0.048226)  #3
  bsearch         0.010349   0.000000   0.010349 (  0.010362)  #2

benchem([*10001..100000])
    user           system     total      real
  enumerate     147.814253   0.000000 147.814253 (147.833672)  #4
  integer_sqrt    0.036118   0.000000   0.036118 (  0.036115)  #1
  prime_divisors  0.889245   0.000000   0.889245 (  0.889367)  #3
  bsearch         0.127438   0.000000   0.127438 (  0.127459)  #2

Upvotes: 1

steenslag
steenslag

Reputation: 80065

Ruby has Integer::sqrt since version 2.5

def perfect_square(num)
  Integer.sqrt(num)**2 == num
end

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110685

For fun, here's another way:

def perfect_square(num)
  (1..1+num/2).find { |n| n*n >= num }**2 == num
end

(1..200).select { |num| perfect_square(num) }
  #=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196]

For greater efficiency the operative line could be replaced with

  (1..1+num/2).bsearch { |n| n*n >= num }**2 == num

See Array#bsearch.

Upvotes: 1

iGian
iGian

Reputation: 11193

Since the product of perfect squares is a perfect square, using Prime#prime_division:

require 'prime'

def perfect_square?(n)
  n.prime_division.all? { |_, exp| exp % 2 == 0 }
end

Upvotes: 2

Kien Le
Kien Le

Reputation: 191

You can do this instead

def perfect_square(num)
  !(1..num).find { |n| n * n == num }.nil?    
end

Find any number from 1 to num that its square is num, check if it's not nil.

Upvotes: 0

Jörg W Mittag
Jörg W Mittag

Reputation: 369458

Newlines are optional in Ruby, so you can always trivially implement anything, no matter how complex, as a one-liner by simply removing all newlines, potentially replacing them with the appropriate alternative separator:

def perfect_square(num) (1..num).each {|n| return true if n*n == num }; false end

Upvotes: 1

Related Questions