【问题标题】:Range checks using a switch statement使用 switch 语句进行范围检查
【发布时间】:2015-10-09 12:04:21
【问题描述】:

我的老师指定了一个程序来同时使用if-else 语句和switch 语句,因此我们了解如何实现这两者。该程序要求我们提示用户分别以磅和米为单位输入他们的体重和身高。这是我的尝试:

没有开关

#include "stdafx.h"
#include <iostream>

using namespace std;


int main()
{
    double height, weight, BMI, heightMeters, weightKilo;
    const double KILOGRAMS_PER_POUND = 0.45359237;
    const double METERS_PER_INCH = 0.0245;

    cout << "Please enter your height (inches) and weight (pounds)" << endl;
    cin >> height >> weight;

    weightKilo = weight*KILOGRAMS_PER_POUND;
    heightMeters = height*METERS_PER_INCH;
    BMI = weightKilo / (heightMeters*heightMeters);

    if (BMI < 18.5) {
        cout << "You are underweight " << endl;
    }
    else if (BMI >= 18.5 && BMI < 25.0) {
        cout << "You are normal" << endl;
    }
    else if (BMI >= 25.0 && BMI < 30.0) {
        cout << "You are overweight" << endl;
    }
    else if (BMI >= 30.0 && BMI < 35) {
        cout << "You are obese" << endl;
    }
    else {
        cout << "You are gravely overweight" << endl;
    }
}

带开关

#include "stdafx.h"
#include <iostream>

using namespace std;


int main()
{
    double height, weight, heightMeters, weightKilo;
    int BMI, q;
    const double KILOGRAMS_PER_POUND = 0.45359237;
    const double METERS_PER_INCH = 0.0245;

    cout << "Please enter your height (inches) and weight (pounds)" << endl;
    cin >> height >> weight;

    weightKilo = weight*KILOGRAMS_PER_POUND;
    heightMeters = height*METERS_PER_INCH;
    BMI = weightKilo / (heightMeters*heightMeters);

    if (BMI < 18.5) {
        q = 1;
    }
    else if (BMI >= 18.5 && BMI < 25.0) {
        q = 2;
    }
    else if (BMI >= 25.0 && BMI < 30.0) {
        q = 3;
    }
    else if (BMI >= 30.0 && BMI < 35) {
        q = 4;
    }
    else {
        q = 5;
    }

    switch (q) {
        case 1: cout << "You are underweight" << endl; break;
        case 2: cout << "You are a normal weight " << endl; break;
        case 3: cout << "You are overweight" << endl; break;
        case 4: cout << "You are obese" << endl; break;
        case 5: cout << "You are gravely overweight" << endl; break;
    }
}

这是我想到的方式,包括一个 switch 语句。有没有办法将第一个代码块实现为一个 switch 语句?

我几乎可以肯定,既不能使用范围也不能使用双精度数 (18.5)。我给我的老师发了电子邮件,他们给了我一个大致的答案

这对您来说可能没有意义,但有时您将不得不编写一个没有意义的程序。我并不是说你没有合理的问题,但如果有人能弄清楚你就可以。但是,也许它无法弄清楚。这就是挑战”。

所以,我在问:是否有某种方法可以只对第一个代码块使用 switch 语句,或者我是否做了在代码中使用 switch 语句的最佳方法,即使它根本没有有必要吗?

【问题讨论】:

  • 你不能使用双打开关。
  • 无关:英寸到米的换算为 0.0254 m/in。此外,很高兴在您的代码中看到命名的转换因子。我无法告诉你我在遗留代码中遇到了多少“神奇数字”,我无法弄清楚数字到底意味着什么。
  • “这对你来说可能没有意义,但有时你将不得不编写一个没有意义的程序。” - 告诉一个奇怪的事情学生。
  • @ChristianHackl 是的。我相信它,因为她不知道自己在说什么。
  • @nocomprende,我强烈反对。在研究问题集时,解决方案似乎不能很好地转换为像 C++ 这样的语言,因为您必须考虑 C++ 语言模拟了一个非常低级的机器:指针和位类型等等。不过,用函数式语言编写的解决方案将非常类似于您的问题集的数学模型。

标签: c++ switch-statement


【解决方案1】:

一如既往地在 C++ 中,支持标准库算法。在这种情况下,您想要进行范围查找。这很容易通过有序的边界序列:

double const boundaries[] = { 18.5, 25, 30, 35 };

switch (upper_bound(begin(boundaries), end(boundaries), BMI) - boundaries) {
    case 0: cout << "You are underweight "       << endl; break;
    case 1: cout << "You are normal"             << endl; break;
    case 2: cout << "You are overweight"         << endl; break;
    case 3: cout << "You are obese"              << endl; break;
    case 4: cout << "You are gravely overweight" << endl; break;
};

其实我建议你

观看现场演示on Coliru

#include <iostream>
#include <algorithm>

const char* bmi_classification(double bmi) {
    static double const boundaries[] = { 18.5, 25, 30, 35 };

    double const* lookup = std::upper_bound(std::begin(boundaries), std::end(boundaries), bmi);
    switch (lookup - std::begin(boundaries)) {
        case 0: return "underweight";
        case 1: return "normal";
        case 2: return "overweight";
        case 3: return "obese";
        case 4: return "gravely overweight";
    }
    throw std::logic_error("bmi_classification");
}

int main() {
    for (double BMI : { 0.0, 18.4999, 18.5, 24.0, 25.0, 29.0, 30.0, 34.0, 35.0, 999999.0 }) {
        std::cout << "BMI: " << BMI << " You are " << bmi_classification(BMI) << "\n";
    }
}

打印

BMI: 0 You are underweight
BMI: 18.4999 You are underweight
BMI: 18.5 You are normal
BMI: 24 You are normal
BMI: 25 You are overweight
BMI: 29 You are overweight
BMI: 30 You are obese
BMI: 34 You are obese
BMI: 35 You are gravely overweight
BMI: 999999 You are gravely overweight

奖金

不用switch也可以更优雅:

Live On Coliru

const char* bmi_classification(double bmi) {
    constexpr int N = 5;
    static constexpr std::array<char const*, N> classifications {
        { "underweight", "normal", "overweight", "obese", "gravely overweight" }};
    static constexpr std::array<double, N-1> ubounds {
        { 18.5, 25, 30, 35 }};

    auto lookup = std::upper_bound(std::begin(ubounds), std::end(ubounds), bmi);
    return classifications.at(lookup - std::begin(ubounds));
}

【讨论】:

  • 一个漂亮的实现。 OP 将受益于研究每个部分,就像我一样。
  • 非常感谢。我想我很困惑的是如何获得用户输入。当我删除 for 循环时,它会打印出不正确的 bmi 类型。除了upper_bound,还有其他我可以使用的函数吗,因为这只是选择了一个BMI类型,因为它在val上打印了值。 (例如:如果是正常的,则打印超重,减重,则打印正常等)。
  • “0 你很正常”有问题吗?不应该减持吗?
  • @P45Imminent Ooops。是的,删除了无关的 0 边界元素。谢谢!
  • 在你的 switch 语句中,你为什么不写:upper_bound(...) - begin(boundaries) 在我看来,这样更通用。
【解决方案2】:

除非你有一个绝对可怕的编译器扩展,否则你不能 switch 在 C++ 的范围内。

但是,如果您创建 BMI 范围的 std::vector,则可以优雅地使用开关:

std::vector&lt;double&gt; v = {18.5, 25.0 /*etc*/}

然后使用std::lower_boundstd::distance 来获取给定BMI 在上述范围内的位置。 这个是你switch的数量。

然后您可以更进一步并定义输出消息的std::vector&lt;std::string&gt;。那么你既不需要switch 也不需要if 块!所有的选择逻辑都委托给std::lower_bound

我故意没有给你完整的代码:我相信这些提示就足够了。

【讨论】:

  • 虽然我不使用 gcc case ranges,但我觉得把这样的扩展称为可怕的评论有点过头了。
【解决方案3】:

我们需要适应输入,所以,而不是这段代码:

if (BMI < 18.5) {
        q = 1;
    }
    else if (BMI >= 18.5 && BMI < 25.0) {
        q = 2;
    }
    else if (BMI >= 25.0 && BMI < 30.0) {
        q = 3;
    }
    else if (BMI >= 30.0 && BMI < 35) {
        q = 4;
    }
    else {
        q = 5;
    }

    switch (q) {
    case 1: cout << "You are underweight" << endl; break;
    case 2: cout << "You are a normal weight " << endl; break;
    case 3: cout << "You are overweight" << endl; break;
    case 4: cout << "You are obese" << endl; break;
    case 5: cout << "You are gravely overweight" << endl; break;

    }

你需要类似的东西

switch (1 + (BMI >= 18.5) + (BMI >= 25) + (BMI >= 30) + (BMI >= 35)) {
    case 1: cout << "You are underweight" << endl; break;
    case 2: cout << "You are a normal weight " << endl; break;
    case 3: cout << "You are overweight" << endl; break;
    case 4: cout << "You are obese" << endl; break;
    case 5: cout << "You are gravely overweight" << endl; break;
}

逻辑是将 if-else 转换为数学公式,返回一个 int。

【讨论】:

  • 难以维护:您必须记住在添加新案例时更新5
  • 确实如此。通常,只要您有解决方案,就有利有弊。我相信这里的想法是有效的,但它是否是最好的,还有待商榷。
  • 从 BMI 数学计算整数是个好主意。在这个特殊的问题中,将来可能不会添加任何案例。 BMI 计算公式不会改变。
  • switch ((BMI &gt;= 18.5)+(BMI &gt;= 25.0)+(BMI &gt;= 30)+...) 怎么样?它看起来更简单。
  • 对基于1的索引说不!
【解决方案4】:

你不能在 switch 中使用 double。文档说:

switch ( expression )
   case constant-expression : statement
   [default   : statement]

表达式必须是整数类型或类类型 其中有一个明确的转换为整数类型。不可缺少的 促销按照积分促销中的说明进行。

附带说明:

有些编译器(如Clang 3.5.1)允许case x ... y 作为C++ 语言的扩展。但这也适用于整数数据类型。类似的东西

switch(x){
       case 0:
            cout << "Test1";
            break;
       case 0 ... 9:
            cout << "Test2";
            break;

【讨论】:

    【解决方案5】:

    C++ 中的开关只允许您检查整数和字符的值。

    BMI 是 double 类型,因此无法在 switch 中检查其值。

    在您使用开关的解决方案中,您还应该将变量 BMI 声明为 double。如果您将其声明为整数,则所有小数结果都将转换为整数,并且您将丢失小数位。

    【讨论】:

      【解决方案6】:

      您可以从数组/向量动态计算案例标签,而不是硬编码 if/else 表达式:

      //#include "stdafx.h"
      #include <iostream>
      
      using namespace std;
      
      
      inline int seg(double d){ //calculate segment for a BMI of d
        constexpr double segs[] = { 18.5, 25, 30, 35 };
        constexpr int n = sizeof(segs)/sizeof(double);
        int r; for(r=0; r<n; r++)
          if(d<segs[r]) return r;
        return r;
      }
      
      int main()
      {
        double height, weight, heightMeters, weightKilo;
        int BMI, q;
        const double KILOGRAMS_PER_POUND = 0.45359237;
        const double METERS_PER_INCH = 0.0245;
      
        cout << "Please enter your height (inches) and weight (pounds)" << endl;
        cin >> height >> weight;
      
        weightKilo = weight*KILOGRAMS_PER_POUND;
        heightMeters = height*METERS_PER_INCH;
        BMI = weightKilo / (heightMeters*heightMeters);
      
      
      
        switch (seg(BMI)) {
          case 0: cout << "You are underweight" << endl; break;
          case 1: cout << "You are a normal weight " << endl; break;
          case 2: cout << "You are overweight" << endl; break;
          case 3: cout << "You are obese" << endl; break;
          case 4: cout << "You are gravely overweight" << endl; break;
        }
      
      }
      

      (如果你真的想的话,你甚至可以创建 seg 函数 constexpr)。

      【讨论】:

      • +1 用于解决问题。 -1 表示代码格式不佳。 -1 用于以更难阅读的方式重新实现 std::upper_bound
      【解决方案7】:

      你可以这样做:

      switch ((round)BMI)
      {
          case 1: case 2: case 3: .... case 15: case 16: case 17: cout<< "You are underweight " << endl; break;
          case 18: ... case 24: cout << "You are normal" << endl; break;
          case 25: ... case 29: cout << "You are overweight" << endl; break;
          case 30: ... case 34: cout << "You are obese" << endl; break;
          default: cout << "You are gravely overweight" << endl;
      }
      

      此外,我不禁注意到,由于您使用的是 if-else,因此您可以避免 else-if 语句中的第一个条件,例如:

      if (BMI < 18.5)
      {
          cout << "You are underweight " << endl;
      }
      else if (BMI < 25.0)
      {
          cout << "You are normal" << endl;
      }
      else if (BMI < 30.0)
      {
          cout << "You are overweight" << endl;
      }
      else if(BMI < 35)
      {
          cout << "You are obese" << endl;
      }
      else
      {
          cout << "You are gravely overweight" << endl;
      }
      

      除此之外,您的两个实现看起来都不错。

      【讨论】:

      • 为 4 行代码输入 34 次 case 对我来说似乎很疯狂。
      • @Teepeemm 简单!使用宏!
      猜你喜欢
      • 2014-08-11
      • 2011-03-16
      • 1970-01-01
      • 2014-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多