1

So I got this code, I input three numbers. Now I want to display them in a for loop. From smallest to biggest, and in another loop from biggest to smallest. How can I do this?

int main()
{

    int num1, num2, num3;
    cout << "Enter first num" << endl;
    cin >> num1;

    cout << "Enter second num" << endl;
    cin >> num2;

    cout << "Enter third num" << endl;
    cin >> num3;
}

I have done this like this but I think it's not a proper way to do it.

for(int i = 0; i <= 0; i++) {
    cout << num1;
    cout << num2;
    cout << num3;
}

for(int i = 0; i <= 0; i++) {
    cout << num3;
    cout << num2;
    cout << num1;
}

Edit:

is this better?

int main()
{
    int numbers[3];

    cout << "Enter first num" << endl;
    cin >> numbers[0];

    cout << "Enter second num" << endl;
    cin >> numbers[1];

    cout << "Enter third num" << endl;
    cin >> numbers[2];

    for (int i = 0; i <= 2; i++)
    {
        cout << numbers[i];
    }

    for (int i = 2; i >= 0; i--)
    {
        cout << numbers[i];
    }

    return 0;
}
anastaciu
  • 23,467
  • 7
  • 28
  • 53
Dave
  • 1
  • 1
  • 9
  • 38
  • How about putting them in an array (or vector) and sorting them before printing, then reversing and print again? – JHBonarius Oct 17 '20 at 12:00
  • I can't use an array with custom numbers, I need numbers to be inputted by user. – Dave Oct 17 '20 at 12:00
  • Are you forced to use for loop? It can be done without using for loop. – sup39 Oct 17 '20 at 12:03
  • I'm not forced to use for loop. Just the way I thought it could be done. I am beginner. Is there alternative? – Dave Oct 17 '20 at 12:04
  • 1
    What do you mean with "can't"? Why? And you can have array elements be assigned by a user with no problem. – JHBonarius Oct 17 '20 at 12:06

3 Answers3

1

You don't need a loop, the one you have it's not really a loop in the sense that it only cycles once, you can use a chained ostream:

cout << num1 << " " << num2 << " " << num3 << "\n";

And

cout << num3 << " " << num2 << " " << num1 << "\n";

But if you want print them sorted by value and you can't use some container where you can apply a sorting algorithm, you'll need some conditionals.

EDIT: Is this better?

Storing it in an array makes it easier to deal with the data, for instance, it will allow you to sort it by value using something as simple as <algorithm> library std::sort.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
1

Given that the input doesn't seem to have any meaning other than "some numbers", you should use a container, and the obvious choice would be:

std::vector<int> nums;

From c++20, you don't needs loops at all for this problem, because you can use ranges:

#include<ranges>
namespace rs = std::ranges;
namespace rv = std::views;

and now you can read in numbers like this:

rs::copy_n(std::istream_iterator<int>(std::cin), 3,
           std::back_inserter(nums));

I'm not sure if you want to use the order that the user inputs the numbers, but if you want the actual smallest to largest, you can do:

rs::sort(nums);

Now to print out the numbers:

rs::copy(nums, 
         std::ostream_iterator<int>(std::cout, " "));

and in reverse:

rs::copy(nums | rv::reverse, 
         std::ostream_iterator<int>(std::cout, " "));

Here's a demo.

cigien
  • 57,834
  • 11
  • 73
  • 112
1

What you are trying to do is very common in programming languages. I.e. enter data, process it and output it in one form or another.

A loop is what you use if you want to execute a piece of code a number of times... a number more than 1 that is. In your first example you execute the loop once, making the extra lines of code a bit redundant.

You already updated your code, showing you quickly realized how to used arrays. These types of arrays (i.e. int numbers[3];) are often referred to as C-style arrays, as they were inherited from C. In C++ we can now use std::array<int, 3> numbers;, which more adheres to the C++ style of working. (The std:: part of the type name indicates the type is define in the standard library namespace. The type is part of the C++ language standard.) The problem with these two types is that they have a static(=fixed) size. I.e. the size has to be know at compile time. This can be quite a limitation if you don't know how many items the user wants to enter. However, the C++ standard defines other container types which can hold a variable amount of items. Of these, std::vector is the dynamic (variable) size counterpart of the array: these both store their items sequentially in memory.

So you can for instance use a loop to add (to the back = push_back()) a number of elements selected by the user to the vector.

#include <vector>
#include <iostream>
[...]
    std::vector<int> numbers;
    std::cout << "How many numbers do you want to enter?\n";
    int N;
    std::cin >> N;
    numbers.reserve(N); // good practice to reserve memory if you know the number of elements
    for (int i = 0; i < N; ++i) {
        std::cout << "Enter a number: ";
        int number;
        std::cin >> number;
        numbers.push_back(number);
    }
[...]

Note that there is no check on the input: e.g. if the user would enter "-1" after the first question, things would break. I will not consider handling user error in my answer.

You can already see some code duplication in here: cout, type definition, cin. You can extract this in a separate function.

#include <string>
[...]
int ReadUserInput(std::string const& message) {
    std::cout << message;
    int value;
    std::cin >> value;
    return value;
}

or even better, you make a function template. I.e. a template for a function: the compiler will generate implementations of this function for you, depending on the type T inferred. I also use the std::string_view now, which can view to different types of strings (std::string, char*)

#include <string_view>
[...]
template<typename T>
T ReadUserInput(std::string_view message = "") {
    if (!message.empty()) std::cout << message; //only print is there's an actual message
    T value;
    std::cin >> value;
    return value;
}

Next, the C++ library has more to offer, including a number of algorithms that are commonly used in programming. One of these is a generator that repeatedly calls a function, of which the result is used to assign successive elements in a container. The object that points to a specific element in the container is called an iterator. The C++ standard library offers a convenient iterator type that executes a push_back: the std::back_inserter. The previous code can now be reduced to:

    int const N = ReadUserInput<int>("How many numbers do you want to enter?\n");
    std::vector<int> numbers;
    numbers.reserve(N); // good practice to reserve memory if you know the number of elements
    std::generate_n(back_inserter(numbers), N, ReadUserInputGenerator<int>("Enter a number: "));

"But wait", you might ask, "what is this ReadUserInputGenerator?". Well, to make the generate_n work, you need to pass a pointer/handle to a generator function, which is then executed for each element. If we'd just call ReadUserInput<int>("Enter a number: "), then the function would already have been evaluated. So we need to add another intermediate function object that makes this generator function. In the passed we would make a class for this

template<typename T>
class ReadUserInputGenerator {
private:
    std::string_view message;
public:
    ReadUserInputGenerator(std::string_view message = "") : message(message) {}
    T operator()() const { return ReadUserInput(message); }
};

... but now we can do using lambda expressions

template<typename T>
auto ReadUserInputGenerator = [](std::string_view message = "") {
    return [message]() { return ReadUserInput<T>(message); };
};

(note to more experienced readers: I'm not really sure about passing string_view by value. I based it on this)

SOOOOO, now we finally have our input. Next you wanted to sort it. Again, this is a common operation. There are actually many ways to sort a collection a values and it is a good excersize to implement these yourself... However, like I mentioned previously, as these kind of operations are common in programming, the C++ standard library actually offers an algorithm for sorting: std::sort.

std::sort(begin(numbers), end(numbers));

^ here begin and end refer to iterators pointing to the begin and end (or actually one past the end) of your vector. You could sort only part of your vector this way. However, the most common case is just begin to end, so in C++20 they've introduced the ranges algorithms, and the previous statement can be reduced to

std::ranges::sort(numbers);

... AAAND now its sorted... printing is next. You can print using a loop... but even there you will have a number of choices. And indexed loop:

for (int i = 0; i < numbers.size(); ++i) {
    std::cout << numbers[i] << ' ';
}

An iterator based for loop:

for (auto it = cbegin(numbers); it != cend(numbers); ++it) {
    std::cout << *it << ' ';
}

Note: the 'c' before begin and end denote that it is a "const" qualified iterator, i.e. it may not modify the contents of the object it points to.

Or a range based for loop:

for (int number : numbers) {
    std::cout << number << ' ';
}

There is also a special convenience iterator type that can push to the cout: std::ostream_iterator. You can copy the vector to this iterator using the algorithm std::copy

std::copy(cbegin(numbers), cend(numbers), std::ostream_iterator<int>(std::cout, " "));

Note, the second argument of the ostream_iterator is the delimiter, i.e. the string appended after each element. Of course there's also a C++20 ranges version.

std::ranges::copy(numbers, std::ostream_iterator<int>(std::cout, " "));

... FINALLY reversing. One option is just to reverse all elements in the vector and print them out again using one of the above mentioned methods. There's of course an algorithm to do so: std::reverse.

std::reverse(begin(numbers), end(numbers));

However, this operation modifies the contents of the container(vector), which might be costly. If you don't want to to this, you'll have to loop though your vector in reverse order

for (int i = numbers.size() - 1; i >= 0; --i) {
    std::cout << numbers[i] << ' ';
}

This looks complex, and it's easy to make errors. You could instead use the reverse iterators of vector, to traverse through the vector in reverse order: (you need to add an 'r' to the begin/end)

for (auto it = crbegin(numbers); it != crend(numbers); ++it) {
    std::cout << *it << ' ';
}

or

std::copy(crbegin(numbers), crend(numbers), std::ostream_iterator<int>(std::cout, " "));

For C++20, there's no range operation to reverse the vector. However, they introduced "views" that are used to observe the values in the vector in a specific way. One such a way is "observe it in reverse order": std::ranges::view::reverse. So in C++20 you will be able to do:

for (int number : numbers | std::views::reverse) {
    std::cout << number << ' ';
}

or

std::ranges::copy(numbers | std::views::reverse, std::ostream_iterator<int>(std::cout, " ")); 

which both don't modify numbers.

The end code could look a little bit something like this (pre C++20 version):

#include <vector>
#include <iostream>
#include <string_view>
#include <iterator>
#include <algorithm>

template<typename T>
T ReadUserInput(std::string_view message = "") {
    if (!message.empty()) std::cout << message; //only print is there's an actual message
    T value;
    std::cin >> value;
    return value;
}

template<typename T>
auto ReadUserInputGenerator = [](std::string_view message = "") {
    return [message]() { return ReadUserInput<T>(message); };
};

int main() {
    int const N = ReadUserInput<int>("How many numbers do you want to enter?\n");
    std::vector<int> numbers;
    numbers.reserve(N); // good practice to reserve memory if you know the number of elements
    std::generate_n(back_inserter(numbers), N, ReadUserInputGenerator<int>("Enter a number: "));
    std::sort(begin(numbers), end(numbers));
    std::copy(cbegin(numbers), cend(numbers), std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';
    std::copy(crbegin(numbers), crend(numbers), std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';
}
JHBonarius
  • 10,824
  • 3
  • 22
  • 41
  • wow you wrote so much. Thanks. I guess the C++ course I done from 2018 is outdated. – Dave Oct 17 '20 at 21:05
  • @Dawid The problem is that there are a lot of bad C++ courses (and teachers) around. I do not claim to be 'the best', but I can at least try to teach you some concepts. – JHBonarius Oct 18 '20 at 08:29
  • Also, why do you write code with `std`? Is it not better to write `using namespace std;` and write without it? Is there any difference, or matter of preference? – Dave Oct 19 '20 at 20:28
  • @Dawid no, it's not. [read this](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – JHBonarius Oct 21 '20 at 17:48