I'm trying to solve a problem :
Your task is to write a simple function that takes a number of meters,
and outputs it using metric prefixes.For this exercise we just want
units bigger than a meter, from meters up to yottameters, excluding
decameters and hectometers.All values passed in will be positive
integers
Examples
meters(51500)
# returns "51.5km"
meters(5000000)
# returns "5Mm"
My code:
def meters(x)
map_prefix={ 24=>'Y', 21=> 'Z', 18=> 'E', 15=> 'P', 12=> 'T', 9=>'G', 6=>'M', 3=>'k',0=>'' }
digits=(x.to_i.to_s.size-1)/3
division=x/(10.0**(3*digits))
"#{division}#{map_prefix[3*digits]}m".sub(/\.0([^\d])/,'\1')
end
It doesn't work for meters(56*10**24) #->expected 56Ym ,instead got 56.000000000004Ym, but it works for bigger numbers such as meters(88*10**24) #->88Ym. The code passes 49 out of 50 tests, can someone help me find the error?
The easiest way to hack your code to get it working seems to avoid float-pointing number, like here:
#!/usr/bin/env ruby
def meters(x)
map_prefix={ 24=>'Y', 21=> 'Z', 18=> 'E', 15=> 'P', 12=> 'T', 9=>'G', 6=>'M', 3=>'k',0$
map_prefix.default = 'Y'
digits = [((x.to_s.size-1)/3)*3, 24].min
division = x.to_s.insert(-digits - 1, '.')
division.sub!(/0+\z/, '')
division.sub!(/\.\z/, '')
"#{division}#{map_prefix[digits]}m"
end
puts meters(51500)
puts meters(5000000)
puts meters(5001)
puts meters(88*10**24)
puts meters(88*10**24 + 1)
puts meters(100)
puts meters(88*10**27)
puts meters(88*10**27 + 1)
With results like:
./ruby.rb
51.5km
5Mm
5.001km
88Ym
88.000000000000000000000001Ym
100m
88000Ym
88000.000000000000000000000001Ym
More seriously, you need to avoid strings whatsoever (no conversions to string at all should be made).
You need arbitrary precision, so float is not an option at all.
I think your issue is that you are multiplying by 10.0, yet you only want to deal with integers.
Something like the following is what you want. (I'm also doing a couple style changes).
def meters(x)
digits=(x.to_i.to_s.size-1)/3
prefix = prefixes[3*digits]
value = x / (10 ** (3 * digits))
"#{value}#{prefix}m".sub(/\.0([^\d])/,'\1')
end
def prefixes
{
24 => 'Y',
21 => 'Z',
18 => 'E',
15 => 'P',
12 => 'T',
9 => 'G',
6 => 'M',
3 => 'k',
0 => ''
}
end
This at least gives the correct solution to the one that is wrong. I'm not going to guarantee it is the correct solution to everything.
I made the hash into its own function here because it seems like it would be static. As well was the other things I mentioned in my comment.
You can use Float#round to round the number to certain digits. For this certain problem, 3 should do fine.
"#{division.round(3)}#{map_prefix[3*digits]}m".sub(/\.0([^\d])/,'\1')
# ^^^^^^^^^
The reason behind the problem is: Float can store integers that's pretty big. However, for integers bigger than a certain limit, Float cannot store them precisely. For [IEEE-754 double precision] floating point, that limit is 253.
FWIW this line will change ruby's duck typing to float. (notice you're introducing 10.0 as a float.)
division=x/(10.0**(3*digits))
When dealing with large numbers, its best to use the built in BigDecimal class. Much cleaner and less error prone, although this is definitely NOT error proof code.
require 'bigdecimal'
def meters(x)
b = BigDecimal.new(x).split
"#{b[1]}#{prefix[b[3] - b[1].length]}m"
end
def prefix
{
24 =>'Y', 21 => 'Z', 18 => 'E', 15 => 'P',
12 => 'T', 9 =>'G', 6 =>'M', 3 =>'k',0 =>''
}
end
UNITS = " kMGTPEZY"
def meters(x)
sx = x.to_s
pwr = sx.size - 1
return sx if pwr < 3
pfx_sz = (pwr < 24) ? (1 + pwr % 3) : pwr - 23
sxs = sx.reverse.to_i.to_s.reverse
sxs = sxs.ljust([sxs.size, pfx_sz].max, '0')
pfx = sxs[0, pfx_sz]
pfx << '.' if (pfx.size < sxs.size)
"#{ pfx }#{ sxs[pfx_sz..-1] }#{ UNITS[[pwr/3, 8].min] }"
end
meters 3 #=> "3"
meters 100 #=> "100"
meters 4000 #=> "4k"
meters 5001 #=> "5.001k"
meters 51500 #=> "51.5k"
meters 5000000 #=> "5M"
meters 88*10**24 #=> "88Y"
meters 88*10**24 + 1 #=> "88.000000000000000000000001Y"
meters 88*10**27 #=> "88000Y"
meters 88*10**27 + 1 #=> "88000.000000000000000000000001Y"
Related
So I am working on a small assignment to transcribe DNA strands to RNA strands. My current code looks like this:
class Complement
def self.of_dna(str)
dna_rna = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
rna = []
str.scan(/[GCTA]/).each do |x|
rna << dna_rna[x]
end
rna.join('')
end
end
It works perfectly, except for in one situation. If a DNA strand is passed that is partially correct, for example ACGTXXXCTTAA, my method will translate the DNA to RNA and just leave out the X's, giving me a result of UGCAGAAUU rather than just "". How can I make it so the loop will fail and exit when it receives a letter that isn't DNA related?
EDIT:
The test I am trying to get to pass looks like this:
def test_dna_correctly_handles_partially_invalid_input
# skip
assert_equal '', Complement.of_dna('ACGTXXXCTTAA')
end
I attempted #Holger Just's idea from below, and received this error:
1) Error:
ComplementTest#test_dna_correctly_handles_completely_invalid_input:
ArgumentError: ArgumentError
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:6:in `block in of_dna'
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:5:in `each'
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:5:in `of_dna'
rna_transcription_test.rb:43:in `test_dna_correctly_handles_completely_invalid_input'
The usual failure I've been getting from the above method is this:
1) Failure:
ComplementTest#test_dna_correctly_handles_partially_invalid_input [rna_transcription_test.rb:48]:
Expected: ""
Actual: "UGCAGAAUU"
Any help would be greatly appreciated.
Try this
class Complement
def self.of_dna(str)
return "" if str =~ /[^GCTA]/
...
end
end
Fun fact, you don't even need a loop to replace characters
str = 'GATTACA'
str.tr('ATCG', 'UAGC')
# => 'CUAAUGU'
Is all you need.
You can also match X in your regex and perform some erorr handling if it is found in the string. This could look something like this:
class Complement
def self.of_dna(str)
dna_rna = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
rna = []
str.scan(/[GCTAX]/).each do |x|
return '' if x == 'X'
rna << dna_rna[x]
end
rna.join('')
end
end
I prefer using Hash#fetch for this because it'll raise a KeyError for you on mismatch, allowing you to write less code that validates inputs (i.e., less defensive programming), which I think is more valuable than cleverness (in which case I would recommend String#tr).
class DNA
TranslationMap = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
attr_reader :dna
def initialize(dna)
#dna = dna
end
def to_rna
dna.each_char.map do |nucleotide|
TranslationMap.fetch(nucleotide)
end.join('')
rescue KeyError
return false
end
end
Feel free to adapt what happens when the error is rescued to fit your needs. I recommend raising a more specific exception (e.g. DNA::InvalidNucleotide) for the caller to handle.
In use:
dna = DNA.new 'GCTA'
# => #<DNA:0x007fc49e903818 #dna="GCTA">
dna.to_rna
# => "CGAU"
dna = DNA.new 'ACGTXXXCTTAA'
# => #<DNA:0x007fc49f064800 #dna="ACGTXXXCTTAA">
dna.to_rna
# => false
I was able to count down to 97 bottle of beers but I am having trouble looping to count down to 1. Is it possible to create a loop with what I wrote? Here's what I have so far.
all_beers = (99).to_s
one_less = ((all_beers).to_i - 1).to_s
puts '' +
all_beers + ' bottles of beer on the wall, ' +
all_beers + ' bottles of beer. You take one down you pass it around ' +
one_less + ', beers on the wall!'
all_beers = one_less
one_less = ((all_beers).to_i - 1).to_s
puts '' +
all_beers + ' bottles of beer on the wall, ' +
all_beers + ' bottles of beer. You take one down you pass it around ' +
one_less + ', beers on the wall!'
use downto :
it will loop from the number you want upto the number you wish.
99.downto(1).each do |s|
all_beers = s
one_less = s - 1
puts '' +
all_beers.to_s + ' bottles of beer on the wall, ' +
all_beers.to_s + ' bottles of beer. You take one down you pass it around ' +
one_less.to_s + ', beers on the wall!'
end
Yes, it is certainly possible. This is taken from 99 Bottles of Beer project:
#
# Rubeer.rb
# by Eric Budd, Jan. 2008
#
# Demonstrates adding functionality to a built-in class, optional method parameters, inline
# conditionals, string replacement, alcohol aversion, and excessively fancy use of hashes.
#
# This borrows the hash from Daniel Straight's excellent implementation for the "wordalize" method
#
class Integer
NUMBER_WORDS = { 0 => "no", 1 => "one", 2 => "two", 3 => "three", 4 => "four", 5 => "five",
6 => "six", 7 => "seven", 8 => "eight", 9 => "nine",
10 => "ten", 11 => "eleven", 12 => "twelve", 13 => "thirteen",
14 => "fourteen", 15 => "fifteen", 16 => "sixteen", 17 => "seventeen",
18 => "eighteen", 19 => "nineteen",
20 => "twenty", 30 => "thirty", 40 => "forty", 50 => "fifty", 60 => "sixty",
70 => "seventy", 80 => "eighty", 90 => "ninety"}
def wordalize
raise "Invalid number to wordalize - should be in the range (0..99)" unless (0..99) === self
return NUMBER_WORDS[self] if self < 20
wordalized = NUMBER_WORDS[self - (self % 10)]
wordalized += '-' + NUMBER_WORDS[self % 10] unless (self % 10) == 0
return wordalized
end
def bottles
raise "Invalid number of bottles - should be in the range (0..99)" unless (0..99) === self
how_many_bottles = self.wordalize + ' bottle'
how_many_bottles += 's' unless self == 1
return how_many_bottles
end
alias :bottle :bottles # for grammar Nazis
end
def sing(number, teetotaller = false)
beverage = teetotaller ? 'coke' : 'beer'
puts "#{number.bottles.capitalize} of #{beverage} on the wall, #{number.bottles} of #{beverage}."
if number != 0
puts "Take one down, pass it around, #{(number - 1).bottles} of #{beverage} on the wall.\n\n"
else
puts "Go to the store and buy some more, 99 bottles of #{beverage} on the wall."
end
end
99.downto(0) { |number| sing(number) }
# Uncomment the following for the alternative teetotaller version
# 99.downto(0) { |number| sing(number, true) }
There are multiple Ruby versions uploaded there, but the default one is too complicated for beginners. This one, though, is very nice, and you should be able to understand what is going on.
The point that should answer your question is the bit with 99.downto(0) { |number| ... }. This is a loop that will repeat anything within the braces (in this case, sing(number)) a hundred times, with number going from 99 to 0.
Also note that it is inefficient (and illegible) to carry around the number as a string ((99).to_s) and convert it back to integer when you need it; rather, have it always be an integer, and convert it to string just before you need it as a string, when you display (or have the string concatenation/interpolation do it for you automatically, as in this piece of code).
While Ruby does have both for and while loops, they are rarely (while) or never (for) used. Instead, Rubyists usually rely on iterators and enumerators. Other functions like Integer#downto are Integer#upto, Integer#times and pretty much everything in the most awesome Enumerable mixin.
The short answer is yes, you can make a loop using what you wrote, and there are many ways of looping with ruby. However, since this seems to be about learning programming, considering that you did not use any control structures or string interpolation, not to mention casting that doesn't make much sense, I'd recommend Why's Poignant Guide to Ruby to learn the concepts of programming while using ruby.
As well as downto, you could do something like:
(1..99).reverse_each do |number|
bottle = number == 1 ? 'bottle' : 'bottles'
verse = "#{number} #{bottle} of beer on the wall, #{number} #{bottle} of beer. "
verse << "Take one down, pass it around, #{number-1} #{number-1 == 1 ? 'bottle' : 'bottles'} of beer on the wall"
puts verse
end
Things you may use to make your life easier: downto(), proc, ternary if (--that--thing--> ?:)
I enjoyed experiencing this exercise for the first time, so I'm a little hesitant about providing an answer here but so it goes.
It's a little bit more advanced, but using a 'proc' to make sure you pluralize "bottle(s)" correctly is a nice and clean way to get it done.
'downto()' is also an awesome way to iterate through those 99 bottles since it makes it feel like you're reading English instead of code.
num_at_start = 99 # You may change this number.
num_bottles = proc { |n| "#{n} bottle#{ n == 1 ? '' : 's'}" }
num_at_start.downto(1) do |num|
print "#{num_bottles.call(num)} of beer on the wall, " +
"#{num_bottles.call(num)} of beer!\n" +
"You take one down, pass it around, "
unless num == 1
puts "#{num_bottles.call(num - 1)} of beer on the wall!"
else
puts "No more bottles of beer on the wall!"
end
end
Source: Learn to Program 2nd Edition by Chris Pine (I changed a couple of things though)
This is the fix that I found for this question:
beer = 99
while beer > 0
puts beer.to_s + " bottles of beer on the wall. " + beer.to_s +
" bottles of beer."
(beer -= 1).to_s
puts "Take one down, pass it around. " + beer.to_s +
" bottles of beer on the wall."
end
I'm trying to write program that computes factorial. However, when I try to run the code below, I get this error: undefined method `*' for nil:NilClass (NoMethodError)
1.upto(number) {|x| a = x==1 ? 1 : a*x }
Am I setting up the ternary operator incorrectly, or is something else wrong?
Thanks for the help
Your ternary operator is set up correctly, but your variable a is not defined at the point where you do the multiplication.
This will work because b has a value:
b = 5
1.upto(number) {|x| a = x==1 ? 1 : b*x }
I'd do it something like:
def factorial(number)
(2 .. number).inject(1) { |m, n| m * n }
end
factorial(1) # => 1
factorial(2) # => 2
factorial(3) # => 6
factorial(5) # => 120
inject is a useful method for things like this, and isn't limited to use with numbers.
It can be written even more concisely:
(1..n).inject(:*) || 1
which comes from "Ruby factorial function". That'll give you something to chew on for a while.
Your code is doing several things that aren't correct:
a isn't ever defined prior to running, so a*x is doomed to fail because you can't multiply a nil by x.
upto will pass the current value into the block as x, but assigning to a will fail because it's always a new local variable a. There is no static memory of a unless you define it outside the block's scope.
In a lot of Ruby iterators, the value of the block can be used as a return value. upto doesn't work that way. You'll get the seed value returned, so, you'd get back 1.
Working out these sort of problems is best done using IRB, which comes with Ruby. Inside the interactive session you can try variations on your code to see what works. It's a lot faster/easier and more convenient than trying to write a script and go through the edit/run cycle.
What about this:
factorial = Hash.new{ |x,y| x[y]= y<2 ? 1 : x[y-1]*y }
Let's test that out by dropping into IRB to see what it does:
>> factorial = Hash.new{ |x,y| x[y]= y<2 ? 1 : x[y-1]*y }
{}
>> factorial[1]
1
>> factorial
{
1 => 1
}
>> factorial[2]
2
>> factorial
{
1 => 1,
2 => 2
}
>> factorial[100]
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
>> factorial
{
1 => 1,
2 => 2,
3 => 6,
4 => 24,
5 => 120,
6 => 720,
7 => 5040,
8 => 40320,
9 => 362880,
10 => 3628800,
...
100 => 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
}
YOW! You're going to compute every intermediate value!? It's going to rapidly consume memory, with very little advantage.
Im currently going through a book and there is a pice of code that I don't quite understand:
class RevealingReferences
attr_reader :wheels
def initialize(data)
#wheels = wheelify(data)
puts data
end
def diameters
wheels.collect do |wheel|
puts "test"
wheel.rim + (wheel.tire*2)
end
end
Wheel = Struct.new(:rim, :tire)
def wheelify(data)
data.collect{|cell|
Wheel.new(cell[0], cell[1])}
end
end
end
puts RevealingReferences.new([3,2,5,8]).diameters
and I get the following output:
3
2
5
8
test
test
test
test
3
2
1
0
1) Now the 3,2,5,8 I understand, but why does not display in array format [3,2,5,8] rather its being displayed one int at a time.
2) Also, in the wheels.collect block, the output prints "test" twice before putting in the output, should it not be "test" value "test" value
3) Also, the answer 3,2,1,0 don't make any sense, when I set #wheels should wheels not be a collection of an array of 2 elements rather then 4?
1) Now the 3,2,5,8 I understand, but why does not display in array
format [3,2,5,8] rather its being displayed one int at a time.
This is due to how puts works. When it sees an array, it prints the #to_s of each element
puts [1,2,3]
# >> 1
# >> 2
# >> 3
If you want it to look like an array, you should inspect it before printing it
puts [1,2,3].inspect
# >> [1, 2, 3]
There's also a shorthand for this, the method p
p [1,2,3]
# >> [1, 2, 3]
2) Also, in the wheels.collect block, the output prints "test" twice
before putting in the output, should it not be "test" value "test"
value
The only thing printing the values is the puts statement on the return value of diameters, so they won't print until after they have been collected. If you wanted to print it after each test, you should probably do something like
def diameters
wheels.collect do |wheel|
puts "test"
p wheel.rim + (wheel.tire*2)
end
end
Which would print:
test
3
test
2
test
1
test
0
3) Also, the answer 3,2,1,0 don't make any sense, when I set #wheels
should wheels not be a collection of an array of 2 elements rather
then 4?
Based on what you're saying here, I assume your data is not in the format you intended. You're passing in [3,2,5,8], but this implies that you meant to pass in [[3,2],[5,8]], or to map across every pair of values:
def wheelify(data)
data.each_slice(2).collect do |cell|
Wheel.new(cell[0], cell[1])
end
end
The reason it isn't doing what you think is because without doing one of these, the cell variable is actually just a number. Since numbers have the brackets method defined on them, they wind up working in this case. But the brackets method just returns 1 or 0, depending on the bit (base 2) at that position:
five = 5
five.to_s(2) # => "101"
five[2] # => 1
five[1] # => 0
five[0] # => 1
So in the case of 3, wheel.rim + (wheel.tire*2) becomes
cell = 3
cell.to_s(2) # => "11"
rim = cell[0] # => 1
tire = cell[1] # => 1
rim + tire * 2 # => 3
And in the case of 2:
cell = 2
cell.to_s(2) # => "10"
rim = cell[0] # => 0
tire = cell[1] # => 1
rim + tire * 2 # => 2
And 5:
cell = 5
cell.to_s(2) # => "101"
rim = cell[0] # => 1
tire = cell[1] # => 0
rim + tire * 2 # => 1
And 8:
cell = 8
cell.to_s(2) # => "1000"
rim = cell[0] # => 0
tire = cell[1] # => 0
rim + tire * 2 # => 0
Which is why diameters returns [3, 2, 1, 0], explaining the last four digits you see.
1) puts will output each argument on a new line, or if the argument is an array, each element of an array on a new line
2) puts "test" is running in the wheels.collect block, there are four Wheel objects created so it outputs four tests while creating the diameters array.
3) The real problem is what seems like a typo either in your book or the transfer of the code to your test environment. I think that last line was meant to read
puts RevealingReferences.new([[3,2],[5,8]]).diameters
Otherwise, the Wheel.new line
Wheel.new(cell[0], cell[1])}
is calling FixNum#[] giving you the n-th bit of the integer. This was a bit of surprise to me too - it seems like a lot could go subtly wrong when accidentally supplying an integer instead of an Array.
With the original code, cell[0] and cell[1] evaluates as 3[0] and 3[1] for the first element of data. With the correction you have the array [3,2][0] => 3, and [3,2][1] => 2 which makes much more understandable code as a "collect" example.
1- collect is a iterator method that accepts a block of code.The collect iterator returns all the elements of a collection.
2- u haven't specified the value to be displayed. do "puts wheel.rim + (wheel.tire*2)".
3- if u print the 'wheel' in the collect block of diameters method, its
"#<struct RevealingReferences::Wheel rim=1, tire=1>"
"#<struct RevealingReferences::Wheel rim=0, tire=1>"
"#<struct RevealingReferences::Wheel rim=1, tire=0>"
"#<struct RevealingReferences::Wheel rim=0, tire=0>"
When the "wheel.rim + (wheel.tire*2)" statement is executed, the result is 3,2,1,0 and each result is returned. if the statement "puts wheel" is added in the collect block for diameter and the prog executed, u wont see the values (3,2,1,0) in the output.
I am new to Ruby and I am trying to write a program which convert Roman numerals to numbers.
This is what I did so far:
roman_numbers = {"M" => 1000, "D" => 500, "C" => 100, "L" => 50, "X" => 10, "V" => 5, "I" => 1}
number_by_user = "MCMXCIX"
singlenum = number_by_user.split(//).reverse!
l = singlenum.length
result =0
result = roman_numbers[singlenum[0]]
puts result
for i in 0..l-1
if roman_numbers.key?(singlenum[i])
**if (roman_numbers[singlenum[i]] > roman_numbers[singlenum[i+1]])** #gives error
result = result - roman_numbers[singlenum[i+1]]
elsif (roman_numbers[singlenum[i]]== roman_numbers[singlenum[i+1]] || **roman_numbers[singlenum[i]] < roman_numbers[singlenum[i+1]])** #gives error
result = result + roman_numbers[singlenum[i+1]]
end
puts roman_numbers[singlenum[i]]
else
puts "One of the values are not roman"
break
end
end
puts "The number is: " , result
but it gives me the following error (see the line with comment):
:in `>': comparison of Fixnum with nil failed (ArgumentError)
You have an off-by-one error. Arrays are 0 indexed. Your singlenum.length in this case is 7, but in your for loop, you go up to 6, then try to reference singlenum[7] to compare to singlenum[6]. singlenum[7] is nil, so it doesn't understand the < operator.
Marc did a good job of explaining where the error was in your code. However, such an error should never have occurred in the first place, since in Ruby, all collections already know how to iterate over themselves: you don't have to do that, so you can never even make such a mistake!
Here's an example of how one might implement the same algorithm in more idiomatic Ruby:
numerals = {
'M' => 1000,
'D' => 500,
'C' => 100,
'L' => 50,
'X' => 10,
'V' => 5,
'I' => 1
}
num = 'MCMXCIX'
(num.chars.map(&numerals.method(:[])) << 0).each_cons(2).inject(0) {|a, (n1, n2)|
if n1 < n2 then a - n1 else a + n1 end
}
See? No loops. No indices. You cannot make an off-by-one error even if you tried!
Bonus: actually, the above snippet does contain a piece of code to prevent an off-by-one error, albeit one at a much higher semantic level. Can you find it?