Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I cannot figure out how to create this conversion, something about math no matter how minute it is rattles my brain.
My class
class Distance
def convert(miles_to_km, km_to_miles)
miles_to_km = 1 * 1.60934
km_to_miles = 1 * 0.621371
end
end
or should it be something like
class Distance
def convert(value, unit)
some data i have yet to understand
end
end
my added input
puts "______________Adding: _____________"
d1 = Distance.new(1, "m")
d2 = Distance.new(1, "k")
puts d1 + d2
d1 = Distance.new(2, "M")
d2 = Distance.new(2, "m")
d3 = d1 + d2
puts d3
puts d3.value
puts d3.unit
d1 = Distance.new(3, "k")
d2 = Distance.new(3, "K")
puts d1 + d2
d1 = Distance.new(4, "k")
d2 = Distance.new(4, "m")
puts d1 + d2
puts "-" * 30 ############
Results should be
______________Adding _____________
1.621371 Miles
4 Miles
4
m
6 Kilometers
10.43736 Kilometers
Your methods need to be specific to your actions.
def convert(miles_to_km, km_to_miles)
miles_to_km = 1 * 1.60934
km_to_miles = 1 * 0.621371
end
Should be
def convert_miles_to_km(km)
return km * 1.60934
end
and vice versa
Parse String Arguments for Flexibility
There are lots of ways to solve this problem. A lot depends one what sort of input you expect from the caller, and what sort of result you want to pass back as your return values.
Sometimes it's useful to be less rigorous about how one expresses units of measurement while still being strict about the way the conversions are performed. Here is an example where a single method takes a string as input, and returns an appropriate floating-point value for predefined units such as kilometers or miles while remaining flexible about the way the distance units are expressed.
# Use constants for your conversion ratios.
MI_TO_KM = 1.60934
KM_TO_MI = 0.621371
# Make your method more flexible by accepting a string
# containing both a distance and a unit identifier.
def convert_distance str
dist, unit = str.scan(/([\d.]+)\s*(\S+)/).flatten
case unit
when /^k/i
dist.to_f * KM_TO_MI
when /^m/i
dist.to_f * MI_TO_KM
else
raise ArgumentError, "unknown unit: #{unit.inspect}"
end
end
This will provide you with the following sample outputs:
convert_distance '10 miles'
#=> 16.0934
convert_distance '10 kilometers'
#=> 6.21371
convert_distance '3mi'
#=> 4.82802
convert_distance '3km'
#=> 1.8641130000000001
convert_distance '1 cubit'
#=> ArgumentError: unknown unit: "cubit"
This type of method isn't necessarily the simplest way to get the job done, but it certainly prevents you from having to define different methods for "km", "kilometers", "klicks", and so forth. It's also easily expandable for other units of distance (e.g. "yards") simply by adding a new clause with the appropriate conversion ratio to the case statement.
Related
I am working on a program that has a component that will generate simulated demographic numbers for various hypothetical jurisdictions.
The methods I have set up to generate each subset of demographics are dependent on some other variables, but generally, most jurisdictions are supposed to look something like:
White - 65%
Latino - 20%
African-American - 10%
Other - 5%
Of course, this isn't always the case. In some scenarios, white may be well under 50% with either Latino or AA being the most significant, but those are more edge cases. But in general that's usually about the balance.
So I am trying to figure out how to generate each demographic, which again is fed from different variables, mostly independently, but ensuring the number always adds up to 100.
I had thought about generating white first, since it's typically the largest, and then just creating a generator where Latino% = 100 - white%*.35 (.35 is just an example here), and so forth, but this creates a situation in which white would always be the plurality, which I don't necessarily want to happen.
So I am a bit stuck. I imagine this is as much a math problem as a Ruby problem. As a non-math person (who, as they have delved into programming, wishes they had paid better attention in class), I'd appreciate any guidance here.
Thank you!
First specify a cumulative distribution function (CDF).
DIST = { white: 0.65, latino: 0.85, aa: 0.95, other: 1.00 }
Note that
DIST[:white] - 0 #=> 0.65
DIST[:latino] - DIST[:white] #=> 0.20
DIST[:aa] - DIST[:latino] #=> 0.10
DIST[:other] - DIST[:aa] #=> 0.05
Now create a method to (pseudo-) randomly select one person from the population and return their ethnicity.
def select_one
rn = rand
DIST.find { |_k, v| rn <= v }.first
end
Try it.
10.times { p select_one }
:white
:aa
:latino
:white
:white
:white
:white
:white
:white
:latino
Now write a method to return a random sample of size n.
def draw_sample(n)
n.times.with_object(Hash.new(0)) { |_, h| h[select_one] += 1 }
end
Try it.
10.times { p draw_sample(100) }
{:white=>66, :latino=>21, :aa=>9, :other=>4}
{:white=>72, :latino=>14, :aa=>11, :other=>3}
{:white=>61, :latino=>19, :aa=>14, :other=>6}
{:white=>64, :latino=>25, :aa=>8, :other=>3}
{:white=>69, :latino=>19, :aa=>4, :other=>8}
{:white=>68, :latino=>17, :aa=>9, :other=>6}
{:white=>68, :latino=>16, :aa=>12 :other=>4}
{:white=>51, :latino=>27, :aa=>10, :other=>12}
{:white=>69, :latino=>23, :aa=>6, :other=>2}
{:white=>63, :latino=>19, :aa=>14, :other=>4}
(Note the order of the keys above varied; I reordered them to improve readability.)
On could alternatively write
def draw_sample(n)
n.times.map { select_one }.tally
end
though this has the disadvantage that it creates an intermediate array.
See Kernel#rand, the form of Hash::new that takes an argument (the default value, here zero) and Enumerable#tally.
From what I understand, each demographic depends on some external variables. What you could do then is
whites = getWhites(); // could be anything really
aa = getAA();
latinos = getLatinos();
sum = whites + aa + latinos;
whites = whites / sum * 100;
aa = aa / sum * 100;
latinos = latinos / sum * 100;
This guarantees that they always sum up to 100
Edit: The code is pseudocode (not ruby), assuming floating point data type
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I want to count the number of capital letters to detect the percentage of capital letters in a string. I tried to do it with a regular expression
string.match(/[A-Z]*/), but that will only match the first combination of capital letters.
string.scan() applies to the entire string, and should work for your use-case. The following should work:
your_string = "Hello World"
capital_count = your_string.scan(/[A-Z]/).length
Here are some ways that do not involve converting the string to an array of characters.
CAPS = ('A'..'Z')
ALL_CAPS = CAPS.to_a.join
#=> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CHAR_TO_BIN = 128.times.with_object({}) do |i,h|
c = i.chr
h[c] = (CAPS.cover?(c) ? 1 : 0)
end
#=> {"\x00"=>0, "\x01"=>0, "\x02"=>0,...," "=>0, "!"=>0,...,
"0"=>0, "1"=>0,..."9"=>0, ":"=>0, ";"=>0, "<"=>0, "="=>0,
">"=>0, "?"=>0, "#"=>0, "A"=>1, "B"=>1,..."Z"=>1, "["=>0,...,
"a"=>0, "b"=>0,...,"z"=>0, "{"=>0,...,"\x7F"=>0}
str = "The quick brown dog, 'Lightning', jumped over 'Bubba', the lazy fox"
1: Not very efficient, but Fastest by far and reads well
str.count(ALL_CAPS)
#=> 3
2: Efficient
str.each_char.reduce(0) { |t,c| t + (CAPS.cover?(c) ? 1 : 0) }
#=> 3
3: If you need to do it many times (may be faster than #2)
str.each_char.reduce(0) { |t,c| t + CHAR_TO_BIN[c] }
#=> 3
4: Delete all non-caps and count
str.gsub(/[^A-Z]/,'').size
#=> 3
or delete all caps and count:
str.size - str.gsub(/[A-Z]/,'').size
#=> 3
I thought it would be interesting to compare the efficiency of the various methods suggested.
require 'fruity'
CAPS = ('A'..'Z')
ALL_CAPS = CAPS.to_a.join
#=> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CHAR_TO_BIN = 128.times.with_object({}) do |i,h|
c = i.chr
h[c] = (CAPS.cover?(c) ? 1 : 0)
end
lower = ('a'..'z').to_a
upper = ('A'..'Z').to_a
L = 50_000
U = 10_000
The test string contains L randomly-drawn lower case letters and U randomly-drawn upper-case letters, shuffled.
str = L.times.map {lower.sample}.concat(U.times.map {upper.sample}).shuffle.join
compare do
scan { str.scan(/[A-Z]/).length }
count { str.count(ALL_CAPS) }
reduce { str.each_char.reduce(0) { |t,c| t + (CAPS.cover?(c) ? 1 : 0) } }
hsh { str.each_char.reduce(0) { |t,c| t + CHAR_TO_BIN[c] } }
gsubA { str.gsub(/[^A-Z]/,'').size }
gsubB { str.size - str.gsub(/[A-Z]/,'').size }
end
Running each test 32 times. Test will take about 33 seconds.
count is faster than gsubB by 39x ± 10.0
gsubB is similar to scan
scan is faster than gsubA by 3x ± 1.0
gsubA is similar to hsh
hsh is similar to reduce
I was amazed by how fast String#count is. I had assumed that Ruby would do an include? for every character in the string. I was wrong. Looking at the source code there is a C function tr_setup_table, suggesting that Ruby is constructing a hash or something similar before doing the counting.
Help me refactor implementing Luhn algorithm, which is described as follows:
The formula verifies a number against its included check digit, which
is usually appended to a partial account number to generate the full
account number. This account number must pass the following test:
From the rightmost digit, which is the check digit, moving left, double the value of every second digit; if the product of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then sum the digits of the products (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9).
Take the sum of all the digits.
If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
Assume an example of an account number "7992739871" that will have a
check digit added, making it of the form 7992739871x:
Account number 7 9 9 2 7 3 9 8 7 1 x
Double every other 7 18 9 4 7 6 9 16 7 2 -
Sum of digits 7 9 9 4 7 6 9 7 7 2 =67
The check digit (x) is obtained by computing the sum of digits then
computing 9 times that value modulo 10 (in equation form, (67 × 9 mod
10)). In algorithm form:
Compute the sum of the digits (67).
Multiply by 9 (603).
The last digit, 3, is the check digit. Thus, x=3.
Following is my implementation, it works but could be a lot better, I believe.
def credit_check(num)
verify = num.to_s.split('').map(&:to_i)
half1 = verify.reverse.select.each_with_index { |str, i| i.even? }
half1 = half1.inject(0) { |r,i| r + i }
# This implements rule 1
half2 = verify.reverse.select.each_with_index { |str, i| i.odd? }
double = half2.map { |n| n * 2 }
double = double.map { |n| n.to_s.split('') }
double = double.flatten.map(&:to_i)
double = double.inject(0) { |r,i| r + i }
final = double + half1
puts final % 10 == 0 && (num.to_s.length > 12 && num.to_s.length < 17) ? "VALID" : "INVALID"
end
I'm a rank noob at all of this, obviously. But I appreciate any help, including proper style!
Suggestions:
Try to encapsulate your code in a class and provide a intuitive public API. Hide the inner details of the algorithm in private methods.
Break the rules into small methods in the class that has utmost 5 lines, break this rule sparingly. Follow Sandi Metz Rules.
Study the problem and find domain names relevant to the problem; use it to name the small methods.
Focus on readability. Remember this quote: "Programs must be written for people to read, and only incidentally for machines to execute." by Hal Abelson from SICP.
Read Ruby style guide to improve code formatting; and yes get a better editor.
Following these may seem like making the code more verbose. But it will improve readability and help for maintenance. Also, if you tend to follow it even in personal projects, this process will be etched into you and will soon become second nature.
With these in mind, go through the following attempt at the problem:
class CreditCard
VALID_LENGTH_RANGE = 12..17
def initialize(number)
#number = number.to_s
end
def valid?
valid_length? && check_sum_match?
end
private
def valid_length?
VALID_LENGTH_RANGE.include? #number.length
end
def check_sum_match?
check_sum.end_with? check_digit
end
def check_sum
digits = check_less_number
.reverse
.each_char
.each_with_index
.map do |character, index|
digit = character.to_i
index.even? ? double_and_sum(digit) : digit
end
digits.reduce(:+).to_s
end
def check_less_number
#number[0..-2]
end
def check_digit
#number[-1]
end
def double_and_sum(digit)
double = digit * 2
tens = double / 10
units = double % 10
tens + units
end
end
Hence you can use it as follows:
CreditCard.new(222222222224).valid? # => true
CreditCard.new(222222222222).valid? # => false
how about using nested inject method
half2 = verify.reverse.select.each_with_index { |str, i| i.odd? }
double = half2.map { |n| n * 2 }
double = double.inject(0){|x,y| x + y.to_s.split("").inject(0){|sum, n| sum + n.to_i}}
I would implement that algorithm like that:
def credit_card_valid?(num)
digits = String(num).reverse.chars.map(&:to_i)
digits.each_with_index.reduce(0) do |acc, (value, index)|
acc + if index.even?
value
else
double_value = value * 2
if double_value > 9
double_value.to_s.split('').map(&:to_i).reduce(&:+)
else
double_value
end
end
end % 10 == 0
end
Well, that code works for those examples from Wikipedia :)
Here are some advices for you:
get rid of prints/puts to stdin in your function, just return a
value. For this function boolean true/false is good.
ruby community
uses '?' in method names that return false/true
don't forget about
properly formatting your code, but maybe you might've not yet learnt how to do it on Stackoverflow (I haven't yet :)
use 2 spaces for indenting your code
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I want to manipulate a very long number using Ruby. But when I print the number in the screen I get only a few digits out of the original number. So how do I get all the digits I want?
Here is my code
x = Array.new
y = Float(0)
for i in (0..100)
x[i] = 14*2**i
y += x[i]/100**i
end
puts y
#=> 14.2857142857143
The result you get is just a visual representation of a float number. However internally, computers use a format (binary floating-point) that cannot accurately represent a number like 0.1, 0.2 or 0.3 at all.
No matter which code we may provide you, but if a number has infinite representation, every attempt to format or display it in a decimal representation will eventually end with a rouding. It can be 10 digits, 1000 digits, or even 100000000 digits, but it's always a rouding.
Dealing with decimals it's hard. Depending on what you are trying to achieve, you may want to:
consider using BigDecimal (BigDecimal provides similar support for very large or very accurate floating point numbers) or
in case of Money manipulation. you can represent the values as Integers and manipulate integers. Divide the integer by the number of cents just for formatting purpose
Here's an example of using BigDecimal
require 'bigdecimal'
x = []
y = BigDecimal(0)
for i in (0..100)
x[i] = 14*2**i
y += x[i]/100**i
end
puts y
# => 0.14E2
puts y.to_i
# => 14
puts y.to_f
# => 14.0
Here is how I would do by requiring the standard library mathn :
require 'mathn'
x = Array.new
y = Float(0)
for i in (0..100)
x[i] = 14*2**i
y += x[i]/100**i
end
y # => 14.285714285714285
y.round(7) # => 14.2857143
y.round(9) # => 14.285714286
y.round(13) # => 14.2857142857143
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have the following code...
#organisims.each do |organisim|
randomvalue = rand(10)
prediction = organisim.predict
if prediction == randomvalue
organisim.resources += 1
end
if prediction != randomvalue
organisim.resources -= 1
end
end
I am trying to alter the 'organisims' resources if the prediction it makes matches the randomly generated number. It seems to work fine, -1 if no match, +1 if there is a match.
The problem is that when I iterate this array (of organisims), processing their resources, I recieve duplicates in my output, such as
Cycle 100
Average resouces: 1500
Cycle 101
Average resouces: 1500
Then again,
Cycle 102
Average resouces: 1400
Cycle 103
Average resouces: 1400
Is this an issue with my code (I see no issues with it) or with the psudorandom number generator that Ruby uses?
Cheers as always
Martin
I think this may be an issue of the scope of your accumulator consider this example.
# example Organism class
class Organisim
attr_accessor :predict, :resources, :prediction
def initialize
#resources = 0
end
def predict
#prediction = rand(10)
#prediction
end
end
# initialize #organisims
#organisims = []
100.times do
#organisims << Organisim.new
end
puts "!!!! Starting Organisim Specific Run"
# iterate over array tracking organisim's resource
#organisims.each_with_index do |org, i|
# parrallel assignment
r, p = rand(10), org.predict
#ruby ternery operator
(p == r) ? org.resources += 1 : org.resources -= 1
puts "Run #{i} Prediction: #{org.prediction} Instance Resources: #{org.resources} Overall Resources: n/a"
end
puts "!!!! Cumulative Resource Run"
# resources scoped outside the iteration loop as accumulator
overall_resources = 0
# re-initialize #organisims
#organisims = []
100.times do
#organisims << Organisim.new
end
#organisims.each_with_index do |org, i|
# parrallel assignment
r, p = rand(10), org.predict
#ruby ternery operator
#track class level resource
(p == r) ? org.resources += 1 : org.resources -= 1
#iterate accumulator
(p == r) ? overall_resources += 1 : overall_resources -= 1
puts "Run #{i} Prediction: #{org.prediction} Instance Resources: #{org.resources} Overall Resources: #{overall_resources}"
end
The first iteration loop is like (I think) the one that you have in your question but you're changing the resource within the organisim object instance.
The second iteration your accumulator is outside the scope of your iteration so it grows and shrinks as the objects are acted upon. :-)