It's sometimes fun to implement the exercises from K&R in C++. It's certainly interesting to see what C++ makes easier and what's still difficult.
I've had a go at Exercise 1-14 in the second edition:
Exercise 1-14. Write a program to print a histogram of the frequencies of different characters in its input.
The extra challenge from 1-13 also applies here:
It is easy to draw the histogram with the bars horizontal; a vertical orientation is more challenging.
I also chose to make my histogram bars logarithmic.
My code looks fairly slick for gathering the data, but I wasn't very satisfied with the output half:
// A program to print a histogram of the frequencies of different
// characters in its input. K&R(2) exercise 1-14.
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <iostream>
#include <iomanip>
#include <iterator>
#include <map>
#include <ranges>
template<typename T>
using counter = std::map<T, std::size_t>;
static bool isprint(char c)
{
return std::isprint(static_cast<unsigned char>(c));
}
int main()
{
auto hist = counter<char>{};
std::ranges::for_each(std::istreambuf_iterator<char>{std::cin},
std::istreambuf_iterator<char>{},
[&hist](char c){ ++hist[c]; });
auto const keys = hist | std::views::keys;
auto const values = hist | std::views::values;
auto const max_val = std::ranges::max(values);
// width of the scale column
unsigned count_width = static_cast<unsigned>(std::ceil(std::log10(max_val)));
// scale to fit a 24-line display (but just assume it's wide enough)
auto constexpr rows = 23u;
auto const scale = std::log(static_cast<double>(max_val - 1)) / rows;
std::cout << std::fixed << std::setprecision(0);
for (auto i = rows; i; --i) {
// scale bar
auto const threshold = static_cast<std::size_t>(std::exp(i * scale));
std::cout << std::setw(count_width);
if (i % 2 == rows % 2) {
std::cout << threshold;
} else {
std::cout << "";
}
std::cout << '|';
// character columns
for (auto v: values) {
std::cout << (v >= threshold ? '*' : ' ');
}
std::cout << '\n';
}
// histogram key
std::cout << std::setw(count_width + 1) << '|';
for (auto k: keys) {
std::cout << (isprint(k) ? k : ' ');
}
}
When fed its own source code, it produces this output:
406| *
| *
241| *
| *
143| *
| * *
84| * **
| * * * * * * **
50|** * * *** * * ***
|** * * * *** * ** ****
29|** * * * *** * * ** ****
|** ** *** * *** ** * ** ****
17|** ** *** * * *** ** **** ****
|** ** * ***** ** ******* ***** ****
10|** *** * ***** ** ******* ***** ****** * *
|** * *** * ***** ** ******* ***** ******* * *
6|** * *** * ***** ** ************* ******** * *
|** * *** * * ** ***** **************** ******** ***
3|** * *** *** * ** ***** **************** ******** ***
|****************** ******* ** ***************** ************
2|****************** ******* ** ***************** ************
|**************************************************************
1|**************************************************************
| "#%&'()*+,-./01234:;<=>?AKRT[\]_abcdefghiklmnopqrstuvwxyz{|}
N.B. The C++20 library support in GCC 11 is not sufficiently complete for this, so GCC 12 is required (or any other compiler with good C++20 support).