2017-02-28 3 views
4

私は実際にはC++で範囲に適切に文字を割り当てるための最良の方法は何かを考えています。範囲を値にマッピングする方法は?

たとえば、私たちはその規模を持っている:

enter image description here

を私たちは、最も簡単な方法で割り当てを行うことができます。

if(a < 40) 
    return 'T'; 
else if(a < 55) 
    return 'D'; 
else if(a < 70) 
    return 'P'; 
else if(a < 80) 
    return 'A'; 
else if(a < 90) 
    return 'E'; 
else if(a <= 100) 
    return 'O'; 

はしかし、あなたはこれを行うために、いくつかのより良いアイデアを持っていますか?

そして、私たちはもっと大きな数字と手紙を持っています(私は文がまだ迷惑になることがあると思います...)。範囲間に空きスペースがある場合はどうなりますか? 30-40 45-55 60-70?そのような小数値について

+1

これはこのテーブルかルックアップテーブルのいずれかですが、この方法は6レコードしかないので大丈夫です。しかし、マジックナンバーをリファクタリングしたいかもしれません。 –

+6

次の条件を否定する必要はありません。 'else'があります。もう一度、' return'があるので、そこにいる必要はありません。 – LogicStuff

+2

@LogicStuff恐らく明白です。 –

答えて

4

編集、簡単なソートされた範囲については、アレイおよび出力

char getGrade (int grade) { 
    int upper[] = { 40, 55, 70, 80, 90, 100 }; 
    int lower[] = { 0, 40, 55, 70, 80, 90 }; 
    char grades[] = { 'T', 'D', 'P', 'A', 'E', 'O' }; 

    for (int i = 0; i< 6; i++) 
     if ((grade< upper[i]) && (grade >= lower[i]))    
      return grades[i]; 
    return 'X'; // no grade assigned 
} 

を使用することができます。@YSC

によって私は structで面白いの実装を追加していると std::find_ifが提案
#include <iostream> 
#include <algorithm> 
#include <vector> 

struct Data{ int lower; int upper; char grade; }; 

char getGrade (int grade, std::vector<Data> data) { 
    auto it = std::find_if(
      data.begin(), 
      data.end(), 
      [&grade](Data d) { return (d.lower<= grade) && (d.upper > grade); } 
      ); 
    if (it == data.end()) 
     return 'X'; // not found 
    else 
     return it->grade; 
} 

int main() { 
    const std::vector<Data> myData = { { 0, 40, 'T'} , { 40, 55, 'D'}, {55, 70, 'P'}, {70, 80, 'A'}, {80, 90, 'E'}, {90, 101, 'O'} }; 
    std::cout << getGrade(20, myData) << std::endl; 
    return 0; 
} 
+0

ニースの答えは、ありがとう、最後に追加された要件で私の質問の一番下を見て、どのように無料のギャップを尊重するためにこのコードを変更することができますか? – BartekPL

+0

私は私の答えを編集しました。下限のために別の配列を追加することができます。 –

+1

読みやすくするために、あなたの配列を 'struct def {int lower; int upper;チャールグレード; } = {{0、40、 'T'}、...}; ' – YSC

3

、Iは、ルックアップテーブルを使用したい:

static const char map[] = 
    "............" // make 40 of them 
    "TTTTTTTTTTTTTTT" // 40..54 
    "DDDDDDDDDDDDDDD" // 55..69 
    // etc 
    ; 

return map[a]; 

大きいか負数またはsparsier範囲のための代替は、実際std::mapを使用することである。

#include <map> 
static const std::map<int, char> map = { 
    { 40, 'T' }, 
    { 41, 'T' }, 
    // ... 
    { 100, 'O' } 
}; 
return map.at(a); 

LogicStuffで提案されているより読みやすい解決策は、範囲を定義し、std::lower_boundで適切な値を見つけることです:

#include <iostream> 
#include <algorithm> 

char grade(int a) 
{ 
    constexpr std::pair<int, char> bounds[] = { 
     { 39, 'T' }, 
     { 54, 'D' }, 
     { 69, 'P' }, 
     { 79, 'A' }, 
     { 89, 'E' }, 
     { 99, 'O' }, 
     { 100, '\0' }, 
    }; 

    return std::lower_bound(
     std::begin(bounds), 
     std::end(bounds), 
     std::make_pair(a, '\0') 
    )->second; 
} 

Test (demo)

grade(39): T 
grade(40): D 
grade(54): D 
grade(55): P 
grade(69): P 
grade(70): A 
grade(79): A 
grade(80): E 
grade(89): E 
grade(90): O 
grade(99): O 
grade(100): 
+1

\ * nodes \ *のセットの 'std :: lower_bound'についてはどうですか? – LogicStuff

+0

'std :: lower_bound'は私のために新しいことです、素敵です – BartekPL

+0

@LogicStuff私は自分の答えにあなたの提案を追加しました。あなたが念頭に置いていたものですか? – YSC

2

あなたは空想になりたいと簡単に拡張可能リストを持っているのが好きなら、あなたはそれを介してルックアップテーブルとだけループを使用することができますの

#include <iostream> 
#include <utility> 
#include <vector> 

char returnGrade(int a) 
{ 
    std::vector<std::pair<char, int>> chars = { 
     std::make_pair('T', 40), 
     std::make_pair('D', 55), 
     std::make_pair('P', 70), 
     std::make_pair('A', 80), 
     std::make_pair('E', 90), 
     std::make_pair('O', 100) 
    }; 
    for(auto itr = chars.begin(); itr != chars.end(); ++itr) 
    { 
     if(a < itr->second) 
      return itr->first; 
    } 
    //return the last one if we passed the loop. 
    return chars.back().first; 
} 

int main() { 
    //test it.. 
    std::cout << returnGrade(20) << " " << returnGrade(45) << " " << returnGrade(90) << " " << returnGrade(100); 
    return 0; 
} 

もちろん、関数のローカルルックアップテーブルを持ってはいけませんが、あなたはそのアイデアを得ます。

+0

しかし、範囲の間に空きスペースがあるとどうなりますか? 30-40 45-55 60-70、私のコードの前のバージョンに戻ります。私はあるスマートな方法でマップを使用できるかどうか疑問に思います。 – BartekPL

+1

@BartekPLギャップ(例えば41..44)に_something_を返す必要があるので、マップの要素として_something_を追加してください( 'std :: make_pair(_something_、41)')。 – TripeHound

+0

@TripeHound、はい私はその方法を知っています、そして、このコードのための良いオプションと思われます – BartekPL

0

あなたが使用することができません、単純arrayと単純for

int array[ 8 ] = { 0, 40, 55, 70, 80, 90, 100, 101 }; 
char score[ 7 ] = { 'T', 'D', 'P', 'A', 'E', 'O', 'O' }; 

int input = 50; 
for(int index = 0; index < 8; ++index){ 
    if(input < array[ index ]){ 
     std::cout << score[ index - 1 ]; // D 
     break; 
    } 
} 

NOTE

  1. 代わりのstd:coutあなたも100自体を一致させるためだけに使用return
  2. 余分O101を使用することができます。
+0

それは悪いバージョンのRobert Eckhaus答え – BartekPL

+0

@ BartekPLどこに問題がありますか? –

+0

あなたはちょうどあなたの答えに自由ギャップの要件を含めることはできませんが、私の質問からのテーブルは完璧な答えです:) – BartekPL

3

これはC++ 14の回答です。すべてがC++ 11に変換できますが、それほど簡単ではありません。

template<class F, class Base=std::less<>> 
auto order_by(F&& f, Base&& b={}) { 
    return 
    [f=std::forward<F>(f), b = std::forward<Base>(b)] 
    (auto const& lhs, auto const& rhs) 
    ->bool 
    { 
     return b(f(lhs), f(rhs)); 
    }; 
} 

order_by投影および任意比較関数オブジェクトを受け取り、std::less<>または比較関数オブジェクトのいずれか、その後の投影を適用比較関数オブジェクトを返します。

ソートや検索に便利です.C++アルゴリズムは比較関数オブジェクトを必要とするため、投影は簡単に記述できます。

template<class A, class B> 
struct binary_overload_t:A,B{ 
    using A::operator(); 
    using B::operator(); 
    binary_overload_t(A a, B b):A(std::move(a)), B(std::move(b)) {} 
}; 
template<class A, class B> 
binary_overload_t< A, B > 
binary_overload(A a, B b) { 
    return { std::move(a), std::move(b) }; 
} 

binary_overloadは、関数オブジェクトをオーバーロードできます。

template<class T> 
struct valid_range_t { 
    T start, finish; 
}; 

これは有効な範囲を表します。私はちょうどstd::pairを使うことができましたが、私は意味のあるタイプを好みます。今

template<class T, class V> 
struct ranged_value_t { 
    valid_range_t<T> range; 
    V value; 
}; 
template<class T, class It> 
auto find_value(It begin, It end, T const& target) 
-> decltype(std::addressof(begin->value)) 
{ 
    // project target into target 
    // and a ranged value onto the lower end of the range 
    auto projection = binary_overload(
    [](auto const& ranged)->T const& { 
     return ranged.range.finish; 
    }, 
    [](T const& t)->T const& { 
     return t; 
    } 
); 
    // 
    auto it = std::upper_bound(begin, end, 
    target, 
    order_by(projection) 
); 
    if (it == end) return nullptr; 
    if (target < it->range.start) return nullptr; 
    return std::addressof(it->value); 
} 

find_valueは非重複範囲に配置ranged_value_t型構造にイテレータのペアをとります。

次に、(半開きの)範囲にtargetが含まれている最初の(したがって唯一の)値のエントリへのポインタを返します。

ranged_value_t<int, char> table[]={ 
    {{0,40}, 'T'}, 
    {{41,55}, 'D'}, 
    {{56,70}, 'P'}, 
    {{71,80}, 'A'}, 
    {{81,90}, 'E'}, 
    {{91,101}, 'O'} 
}; 

auto* ptr = find_value(std::begin(table), std::end(table), 83); 
if (ptr) std::cout << *ptr << "\n"; else std::cout << "nullptr\n"; 

Live example

選択肢を超えるこの回答の利点:

  • 私たちは、不要な値オブジェクトを作成しないでください。値オブジェクトが高価であるか、大規模であるか、または非自明で構成可能である場合、これは重要です。
  • テーブルを作成するための構文は単純です
  • テーブルはほぼすべての形式で使用できます。あなたはファイルからそれらを解析することができます。 find_value関数は単に反復子を取ります(ランダムアクセスを優先します)。
  • 下限を省略できるようにして追加することができます。 valid_range_tにフラグを追加するか(オプションで使用)、sにチェックするとfind_valueに消費し、使いやすくするにはvalid_range_tにコンストラクタを追加する必要があります。

ハーフオープン間隔とクローズ間隔の両方をサポートするように拡張すると、少しの作業が必要になります。私はそれを2番目のチェックとしてfind_valueにハックするように誘惑されるでしょう。

オーバーラッピング間隔にも少し時間がかかります。私は開始時にlower_boundを行い(s)、終了時にupper_boundを行う(f)。

私はこの種のものがデータ駆動型設計に最も適していることを発見しました。これをC++コードでハードコーディングするのは悪い計画です。代わりに、構成を消費し、その構成によって検証され駆動されるコードを記述します。

+1

私の好きなタイプの過剰殺し –

+0

ルックアップテーブルを考えるのに1時間以上必要な場合は、後でそれを維持すると、先週雇った新しい男はいつかあなたを憎むでしょう。ほとんどの場合、シンプルで不器用なものは賢明で美しいものよりも優れています。 – YSC

関連する問題