App Academy's practice test says their chosen way of finding if an input is a power of 2 is to keep dividing it by 2 on a loop and check whether the end result is 1 or 0 (after having tested for the numbers 1 and 0 as inputs), which makes sense, but why won't this way work?
def try
gets(num)
counter = 0
go = 2 ** counter
if num % go == 0
return true
else
counter = counter + 1
end
return false
end
I can't figure out why this won't work, unless the counter isn't working.
There are a number of problems with your code.
First of all, there is no loop and your counter will reset to zero each time if you intend to use the method in a loop, because of counter = 0.
counter = 0; go = 2 ** counter basically means go = 2 ** 0 which is 1. Therefore num % 1 will always be 0
You actually need to divide the number and change it in the process. 12 % 4 will return 0 but you don't know by that if 12 is a power of 2.
IO#gets returns a string and takes a separator as an argument, so you need to use num = gets.to_i to actually get a number in the variable num. You are giving num to gets as an argument, this does not do what you want.
Try:
# Check if num is a power of 2
#
# #param num [Integer] number to check
# #return [Boolean] true if power of 2, false otherwise
def power_of_2(num)
while num > 1 # runs as long as num is larger than 1
return false if (num % 2) == 1 # if number is odd it's not a power of 2
num /= 2 # divides num by 2 on each run
end
true # if num reached 1 without returning false, it's a power of 2
end
I add some checks for your code. Note, that gets(num) returns a String. Your code is fine, but not for Ruby. Ruby hates type-cross-transform like Perl does.
def try(num = 0)
# here we assure that num is number
unless (num.is_a?(Integer))
puts "oh!"
return false
end
counter = 0
go = 2 ** counter
if num % go == 0
return true
else
counter = counter + 1
end
return false
end
The general problem is "how string could use '%' operator with number?"
Try some code in the interpretator (irb):
"5" % 2
or
"5" % 0
Related
I have been attempting the test below on codewars. I am relatively new to coding and will look for more appropriate solutions as well as asking you for feedback on my code. I have written the solution at the bottom and for the life of me cannot understand what is missing as the resultant figure is always 0. I'd very much appreciate feedback on my code for the problem and not just giving your best solution to the problem. Although both would be much appreciated. Thank you in advance!
The test posed is:
If we list all the natural numbers below 10 that are multiples of 3 or
5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Finish the solution so that it returns the sum of all the multiples of
3 or 5 below the number passed in. Additionally, if the number is
negative, return 0 (for languages that do have them).
Note: If the number is a multiple of both 3 and 5, only count it once.
My code is as follows:
def solution(number)
array = [1..number]
multiples = []
if number < 0
return 0
else
array.each { |x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
return multiples.sum
end
In a situation like this, when something in your code produces an unexpected result you should debug it, meaning, run it line by line with the same argument and see what each variable holds. Using some kind of interactive console for running code (like irb) is very helpfull.
Moving to your example, let's start from the beginning:
number = 10
array = [1..number]
puts array.size # => 1 - wait what?
puts array[0].class # => Range
As you can see the array variable doesn't contain numbers but rather a Range object. After you finish filtering the array the result is an empty array that sums to 0.
Regardless of that, Ruby has a lot of built-in methods that can help you accomplish the same problem typing fewer words, for example:
multiples_of_3_and_5 = array.select { |number| number % 3 == 0 || number % 5 == 0 }
When writing a multiline block of code, prefer the do, end syntax, for example:
array.each do |x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
end
I'm not suggesting that this is the best approach per se, but using your specific code, you could fix the MAIN problem by editing the first line of your code in one of 2 ways:
By either converting your range to an array. Something like this would do the trick:
array = (1..number).to_a
or by just using a range INSTEAD of an array like so:
range = 1..number
The latter solution inserted into your code might look like this:
number = 17
range = 1..number
multiples = []
if number < 0
return 0
else range.each{|x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
multiples.sum
#=> 60
The statement return followed by end suggests that you were writing a method, but the def statement is missing. I believe that should be
def tot_sum(number, array)
multiples = []
if number < 0
return 0
else array.each{|x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
return multiples.sum
end
As you point out, however, this double-counts numbers that are multiples of 15.
Let me suggest a more efficient way of writing that. First consider the sum of numbers that are multiples of 3 that do not exceed a given number n.
Suppose
n = 3
m = 16
then the total of numbers that are multiples of three that do not exceed 16 can be computed as follows:
3 * 1 + 3 * 2 + 3 * 3 + 3 * 4 + 3 * 5
= 3 * (1 + 2 + 3 + 4 + 5)
= 3 * 5 * (1 + 5)/2
= 45
This makes use of the fact that 5 * (1 + 5)/2 equals the sum of an algebraic series: (1 + 2 + 3 + 4 + 5).
We may write a helper method to compute this sum for any number n, with m being the number that multiples of n cannot exceed:
def tot_sum(n, m)
p = m/n
n * p * (1 + p)/2
end
For example,
tot_sum(3, 16)
#=> 45
We may now write a method that gives the desired result (remembering that we need to account for the fact that multiples of 15 are multiples of both 3 and 5):
def tot(m)
tot_sum(3, m) + tot_sum(5, m) - tot_sum(15, m)
end
tot( 9) #=> 23
tot( 16) #=> 60
tot(9999) #=> 23331668
I wrote a simple script to sum all digits of positive integer input until 1 digit is left ( for example for input 12345 result is 6 because 1+2+3+4+5 = 15 and 1+5 = 6). It works but is it better way to do that? ( more correct?)
here is a code:
def sum(n)
string=n.to_s
while string.length > 1 do
result=string.chars.inject { |sum,n| sum = sum.to_i + n.to_i}
string=result.to_s
end
puts "Sum of digits is " + string
end
begin
p "please enter a positive integer number:"
number = Integer(gets.chomp)
while number<0
p "Number must be positive!Enter again:"
number = Integer(gets.chomp)
end
rescue
p "You didnt enter integer!:"
retry
end
sum(number)
According to Wikipedia, the formula is:
dr(n) = 1 + ((n − 1) mod 9)
So it boils down to:
def sum(n)
1 + (n - 1) % 9
end
To account for 0, you can add return 0 if n.zero?
You could use divmod (quotient and modulus) to calculate the digit sum without converting to / from string. Something like this should work:
def sum(number)
result = 0
while number > 0 do
number, digit = number.divmod(10)
result += digit
if number == 0 && result >= 10
number = result
result = 0
end
end
result
end
sum(12345) #=> 6
The line
number, digit = number.divmod(10)
basically strips off the last digit:
12345.divmod(10) #=> [1234, 5]
1234 becomes the new number and 5 is being added to result. If number eventually becomes zero and result is equal or greater than 10 (i.e. more than one digit), result becomes the new number (e.g. 15) and the loops starts over. If result is below 10 (i.e. one digit), the loop exits and result is returned.
Short recursive version:
def sum_of_digits(digits)
sum = digits.chars.map(&:to_i).reduce(&:+).to_s
sum.size > 1 ? sum_of_digits(sum) : sum
end
p sum_of_digits('12345') #=> "6"
Single call version:
def sum_of_digits(digits)
digits = digits.chars.map(&:to_i).reduce(&:+).to_s until digits.size == 1
return digits
end
It's looking good to me. You might do things a little more conscise like use map to turn every char into an integer.
def sum(n)
string=n.to_s
while string.length > 1 do
result = string.chars.map(&:to_i).inject(&:+)
string = result.to_s
end
puts "Sum of digits is " + string
end
You could also use .digits, so you don't have to convert the input into a string.
def digital_root(n)
while n.digits.count > 1
array = n.digits
n = array.sum
end
return n
end
This code is not working. I'm trying to code the Collatz conjecture but the code only seems to run once for the input 8. It prints out 4, 1 ,1 and so that shows it only runs for one step. The else block is never executed either. Can someone tell me what's wrong with this Ruby code? I have no idea why it's not working the way it's supposed to.
class Collatz
def collatz(number)
if number == 1
return 0
end
steps = 0
while number > 1 do
if number % 2 == 0
number = number / 2
puts number
steps = steps + 1
puts steps
else
number = (number * 3) + 1
puts number
steps = steps + 1
puts steps
end
return steps
end
end
steps = Collatz.new.collatz(8)
puts steps
end
You have a return statement that's terminating execution after the first iteration of the while loop.
Try
class Collatz
def collatz(number)
return 0 if number == 1
steps = 0
while number > 1 do
if number % 2 == 0
puts number /= 2
puts steps += 1
else
number = (number * 3) + 1
puts number
puts steps += 1
end
end
return steps
end
end
steps = Collatz.new.collatz(8)
puts steps
which returns 3 and prints
4
1
2
2
1
3
[Finished in 0.4s]
And if you want to make your code a little cleaner and more idiomatic, you could refactor it as follows:
class Collatz
def collatz(number)
return 0 if number == 1
steps = 0
while number > 1
number = number.even? ? number / 2 : (number * 3) + 1
puts number, steps += 1
end
steps
end
end
steps = Collatz.new.collatz(8)
#4
#1
#2
#2
#1
#3
#=> 3
Let's properly indent your code and see if we can find the problem:
class Collatz
def collatz(number)
if number == 1
return 0
end
steps = 0
while number > 1 do
if number % 2 == 0
number = number / 2
puts number
steps = steps + 1
puts steps
else
number = (number * 3) + 1
puts number
steps = steps + 1
puts steps
end
return steps
end
end
steps = Collatz.new.collatz(8)
puts steps
end
Look at that — the return is in your while-loop, so it returns at the end of the first iteration. It looks like you actually wanted to end your while loop on the line above that.
This one of the reasons why coding style is really important. It can trick you into thinking your code means something it doesn't.
I have a problem reading a file in ruby.
I am trying to read each line of a file, split it based on characters, and store that into an array. That array, which corresponds to each line, has information. I want to check if that array includes the characters "u" "d" "l" or "r" as you can see below.
IF that line doesn't include ANY of those characters, I increase a count variable by one.
The count -= 1 just takes into account a base case.
My problem is that this gives me a wrong count. For example with a text file that reads:
4 0 0 3 3
0 0 d 0.391538986557049
0 1 ur 63.1258159853081 3.14882640637611
0 2 rd 0.0148854629087619 0.019301544005463
0 3 u 15.6415340291405
count is supposed to be 0.
def compute_closed(file)
count = 0
while line = file.gets do
array = line.split(//)
answer = array.include?("u" || "d" || "l" || "r")
if answer != true
count += 1
end
end
count -= 1
puts count
end
Maybe you have a reason for splitting the line up into bits, but if you're just checking for those characters somewhere in the line, why not use a regex?
def compute_closed(file)
count = 0
while line = file.gets do
count += 1 if line =~ /[udlr]/
end
count -= 1
puts count
end
If you absolutely need to split the lines up by character, then you may want to use sets instead of arrays:
def compute_closed(file)
count = 0
while line = file.gets do
cmp_set = Set.new ['u', 'd', 'l', 'r']
input_set = Set.new(line.split(//))
if input_set.intersection(cmp_set).size == 0
count += 1
end
end
count -= 1
puts count
end
Edit: after posting I see my answer is essentially the same as #Philip's, but I'll leave it up for the slightly different treatment.
There are many ways to do this. Here's another:
File.read('f1').each_line.reduce(0) {|t,s| t + (s =~ /[udlr]/ ? 0 : 1)} - 1
Let's try it:
text =<<_
Now is the time
for all good
Rubiests to
spend some
time in Hawaii.
_
File.write('f1', text)
File.read('f1').each_line.reduce(0) {|t,s| t+(s =~ /[udlr]/ ? 0 : 1)} - 1 #=> 1
It returns 2 - 1 => 1 because if finds a 'u,' 'd', 'l' or 'r' in all but the the first and last rows.
Initially, I had
File.read(fname).each_line.each_with_object(0) {|s,t|
t += 1 unless s =~ /[udlr]/ } - 1
but t would not increment. I was puzzled, so emailed my friend #ArupRakshit, who I can always count on to know the answer, or dig until he finds it. It turns out that in
...each_with_object(memo) {|s,memo|
memo must be a mutable object, an important difference between that method and reduce/inject (which is not made clear in the Ruby docs for Enumerable#each_with_object). Thanks, Arup.
What's wrong with this Ruby code? I'm trying to solve the first Project Euler question.
I think the problem is in the syntax of sum += num, but I can't figure out what the proper syntax for this would be.
sum = 0
num = 0
num2 = 0
loop do
num += 1
if num % 3 == 0
sum += num
break if num > 1000
end
end
loop do
num2 += 1
if num2 % 5 == 0
sum += num2
break if num2 > 1000
end
end
puts sum
Here's an alternative:
(1...1000).select { |x| x % 3 == 0 || x % 5 == 0 }.reduce(:+)
You are making this way more complicated than it needs to be. Also, if the number is a multiple of 3 and 5, it gets added twice. Try something like this:
sum = 0 # initialize the sum
(1...1000).each { |x| # loop from 1 to 1000
sum += x if x % 3 == 0 || x % 5 == 0 # add the number to the sum if it is
# divisible by 3 or 5
}
puts sum # output the sum
This runs, your syntax is okay, but does not give the right answer because, as mentioned, you add multiples of both 3 and 5 twice, once in the first loop, with num, and the second loop, with num2.
So you have two loops, but you actually only need one.
You only need to consider each number once, you can check it to see if it is a multiple of either 3 or 5. This will solve your double-counting issue and also make your code more concise.
Also, like Doorknob shows, the each syntax would save you some lines on those loops. You could also use the for syntax:
for num in (1..1000)
<stuff here>
end
Check out the kinds of loops in "Loops: How to do thousands of operations with a few lines of code.".