直方图代码解析与简化需求:理解Ruby直方图代码并求简洁实现方案
Hey there! Let's break down your histogram code step by step, then look at a cleaner implementation that does exactly the same thing.
How Your Original Code Works
Let's walk through each part to understand its logic:
The
histogrammethod:- It takes an array, a start/stop range, and a width parameter (
chart). - Counts elements in the array that fall between
startandstop(inclusive) usingarray.count { |array| array >= start && array <= stop }—quick note: naming the block variablearrayshadows the method's input parameter, which is confusing and could lead to bugs if you ever modify this code. - Converts the count to a string and right-justifies it to the specified
chartwidth withrjust.
- It takes an array, a start/stop range, and a width parameter (
Variable setup:
- You initialize an empty array, plus control variables for the first interval (
start=1,stop=10), formatting widths (range=1,found=3,chart=2), etc.
- You initialize an empty array, plus control variables for the first interval (
Populating the data array:
- You generate 200 random numbers between 1-100 and push them into the array. However, you call
sort!every time you add a number—this is inefficient, since sorting 200 times does the same job as sorting once after all numbers are added, just with extra work.
- You generate 200 random numbers between 1-100 and push them into the array. However, you call
Generating the histogram output:
- First, you print a header row to label the columns.
- Then you print the first interval (1-10) with its count.
- Next, you loop through every element in the sorted array:
- When you hit an element larger than the current
stop, you start a new interval (add 10 to bothstartandstop), print the new interval's label and count, then a*. - For elements within the current interval, you just print a
*.
- When you hit an element larger than the current
- This works because the array is sorted, so all elements in one interval will print as
*s before you hit the next interval's values.
Cleaner, More Efficient Implementation
Here's a refactored version that matches your original code's functionality perfectly, but with better readability, fewer quirks, and better performance:
# Helper to count elements in a range (no variable shadowing!) def count_in_range(array, start, stop) array.count { |elem| elem.between?(start, stop) } end # Initialize variables with clear, descriptive names sample_size = 200 max_value = 100 interval_size = 10 count_display_width = 2 label_alignment_width = 3 # Generate all random numbers first, then sort ONCE (way more efficient) data = Array.new(sample_size) { rand(1..max_value) }.sort # Print formatted header puts " Range #Found Chart" puts " ------- ------ ------------------------------" puts # Iterate directly over each 10-number interval (1..max_value).step(interval_size) do |start| stop = start + interval_size - 1 element_count = count_in_range(data, start, stop) # Format the range label to match your original output alignment range_label = "#{start.to_s.rjust(label_alignment_width)} - #{stop.to_s.rjust(label_alignment_width)}" # Print the full row: range | count | stars puts "#{range_label} |#{element_count.to_s.rjust(count_display_width)}| #{'*' * element_count}" end
What Makes This Better?
- No variable shadowing: The helper uses
eleminstead of reusingarrayfor the block variable, eliminating confusion. - Faster performance: We sort the array once after generating all numbers, not after every single push—this cuts down on unnecessary computation.
- Self-documenting code: Variable names like
sample_sizeandinterval_sizemake it clear what each value does, unlike vague original names likerangeorfound. - Straightforward interval logic: Instead of looping through every element to trigger new intervals, we explicitly iterate over each 10-number range with
step. This is easier to read and debug. - Simpler star generation: We create the star string directly from the count (
'*' * element_count) instead of looping through every element to print individual*s.
Does This Match Your Original Output?
Absolutely! It generates the exact same histogram: 200 random 1-100 numbers, grouped into 10-number intervals, with right-aligned counts and a * for each element in the interval.
内容的提问来源于stack exchange,提问作者MIkeSmith
相关产品推荐
相关产品推荐

