如何遍历存储在单独变量中的数字?

How to loop through numbers stored in separte variables?

所以我得到了这个代码,我输入了三个数字。现在我想在 for 循环中显示它们。从小到大,在另一个循环中从大到小。我该怎么做?

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;
}

我已经这样做了,但我认为这不是正确的做法。

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;
}

编辑:

这样更好吗?

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;
}

你不需要循环,你拥有的循环并不是真正的循环,因为它只循环一次,你可以使用链式 ostream:

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

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

但是如果你想按值排序打印它们并且你不能使用一些可以应用排序算法的容器,你将需要一些条件。

EDIT: Is this better?

将它存储在一个数组中可以更容易地处理数据,例如,它允许您使用像 <algorithm>std::sort.[= 这样简单的东西按值排序。 16=]

鉴于输入除了“一些数字”之外似乎没有任何意义,您应该使用容器,显而易见的选择是:

std::vector<int> nums;

从 c++20 开始,您根本不需要循环来解决这个问题,因为您可以使用范围:

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

现在您可以阅读这样的数字:

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

我不确定你是否要使用用户输入数字的顺序,但如果你想要实际的最小到最大,你可以这样做:

rs::sort(nums);

现在打印出数字:

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

反之:

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

这里是 demo

您尝试做的事情在编程语言中很常见。 IE。输入数据,对其进行处理并以一种或另一种形式输出。

如果您想多次执行一段代码,则可以使用循环……也就是说,循环次数大于 1。在您的第一个示例中,您执行了一次循环,使额外的代码行有点多余。

您已经更新了代码,表明您很快就了解了如何使用数组。这些类型的数组(即 int numbers[3];)通常被称为 C-style 数组,因为它们是从 C 继承的。在 C++ 中,我们现在可以使用 std::array<int, 3> numbers;,这更坚持 C++ 的工作风格。 (类型名称的 std:: 部分表示该类型是在标准库命名空间中定义的。该类型是 C++ 语言标准的一部分。) 这两种类型的问题在于它们具有静态(=固定)大小。 IE。大小必须在编译时知道。如果您不知道用户要输入多少项目,这可能是一个很大的限制。但是,C++ 标准定义了可以容纳可变数量项目的其他容器类型。其中,std::vector 是数组的动态(可变)大小对应物:它们都将它们的项目按顺序存储在内存中。

因此您可以使用循环将用户选择的一些元素添加(到后面 = push_back())向量中。

#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);
    }
[...]

请注意,没有对输入进行检查:例如如果用户在第一个问题后输入“-1”,事情就会崩溃。我不会考虑在我的回答中处理用户错误。

您已经可以在此处看到一些代码重复:cout,类型定义,cin。您可以在单独的函数中提取它。

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

或者更好的是,你创建一个函数模板。 IE。一个函数的模板:编译器会根据推断出的类型 T 为你生成这个函数的实现。我现在也用std::string_view,可以查看不同类型的字符串(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;
}

接下来,C++ 库提供了更多内容,包括许多 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。以前的代码现在可以简化为:

    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: "));

“等等”,您可能会问,“这是什么 ReadUserInputGenerator?”。好吧,要使 generate_n 起作用,您需要将 pointer/handle 传递给生成器函数,然后为每个元素执行该生成器函数。如果我们只是调用 ReadUserInput<int>("Enter a number: "),那么函数就已经被求值了。所以我们需要添加另一个使这个生成器起作用的中间函数对象。在过去我们会为这个

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

...但现在我们可以使用 lambda expressions

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

(请更有经验的读者注意:我不太确定按值传递 string_view。我基于 this

SOOOOO,现在我们终于有了我们的意见。接下来你想对它进行排序。同样,这是一个常见的操作。居然有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));

^ 这里 beginend 指的是指向向量开始和结束(或者实际上是结束后的一个)的迭代器。您只能通过这种方式对向量的一部分进行排序。然而,最常见的情况是刚开始到结束,所以在 C++20 中他们引入了 ranges 算法,并且前面的语句可以简化为

std::ranges::sort(numbers);

... AAAND 现在已排序...接下来是打印。您可以使用循环打印...但即使在那里您也有多种选择。和索引循环:

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

一个基于for循环的迭代器:

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

注意:begin和end前的'c'表示它是一个"const" qualified迭代器,即不能修改它指向的对象的内容。

或基于循环的范围:

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

还有一个特别方便的迭代器类型可以推送到coutstd::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, " "));

注意,ostream_iterator的第二个参数是分隔符,即每个元素后附加的字符串。当然还有 C++20 ranges 版本。

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

... 终于 倒车了。 一种选择是反转向量中的所有元素,然后使用上述方法之一再次打印出来。当然有一个算法可以做到这一点:std::reverse.

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

但是,此操作会修改容器(vector) 的内容,这可能代价高昂。如果你不想这样做,你将不得不以相反的顺序循环你的向量

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

这看起来很复杂,而且很容易出错。 您可以改为使用向量的 reverse iterators,以相反的顺序遍历向量:(您需要将 'r' 添加到 begin/end)

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

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

对于 C++20,没有范围操作来反转向量。然而,他们引入了"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。所以在 C++20 中你可以这样做:

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

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

两者都不修改 numbers

最终代码可能看起来有点像这样(C++20 之前的版本):

#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';
}