Может ли С++ сделать что-то вроде выражения case ML?

Итак, я несколько раз сталкивался с подобными вещами на С++, где мне бы очень хотелось написать что-то вроде

case (a,b,c,d) of
 (true, true, _, _ ) => expr
 | (false, true, _, false) => expr
 | ...

Но в С++ я всегда получаю что-то вроде этого:

bool c11 = color1.count(e.first)>0;
bool c21 = color2.count(e.first)>0;
bool c12 = color1.count(e.second)>0;
bool c22 = color2.count(e.second)>0;
// no vertex in this edge is colored
// requeue
if( !(c11||c21||c12||c22) )
{
 edges.push(e);
}
// endpoints already same color
// failure condition
else if( (c11&&c12)||(c21&&c22) )
{
 results.push_back("NOT BICOLORABLE.");
 return true;
}
// nothing to do: nodes are already
// colored and different from one another
else if( (c11&&c22)||(c21&&c12) )
{
}
// first is c1, second is not set
else if( c11 && !(c12||c22) )
{
 color2.insert( e.second );
}
// first is c2, second is not set
else if( c21 && !(c12||c22) )
{
 color1.insert( e.second );
}
// first is not set, second is c1
else if( !(c11||c21) && c12 )
{
 color2.insert( e.first );
}
// first is not set, second is c2
else if( !(c11||c21) && c22 )
{
 color1.insert( e.first );
}
else
{
 std::cout << "Something went wrong.\n";
}

Мне интересно, есть ли способ очистить все эти, если и еще, так как это кажется особенно склонным к ошибкам. Было бы еще лучше, если бы можно было заставить компилятор жаловаться, как SML, когда выражение case (или оператор на С++) не является исчерпывающим. Я понимаю, что этот вопрос немного расплывчатый. Может быть, в целом, как можно кратко описать исчерпывающую таблицу истинности с произвольным числом переменных на С++? Спасибо заранее.

3 ответа

С++ традиционно ориентирован на человека, и вы никогда не сможете сделать ничего похожего на следующее, независимо от синтаксиса.

if ([a,b,c,d] == [true,true,false, false]) {}

В стандарте New С++ есть некоторые вещи, которые позволяют вам определять массивы констант inline, и поэтому можно определить класс, который будет принимать в качестве конструктора и поддерживать такие сравнения. Что-то вроде

auto x = multi_val({a,b,c,d});
if (x == multi_val({true, true, false, false}))
{ ... }
else if (x == multi_val(etc.))

Но теперь, чтобы выполнять частичные совпадения, подобные с _, которые не поддерживаются напрямую, и вам придется сделать ваш класс еще более сложным, чтобы вымыться с ним, например, используя шаблон типа шаблона и перейдя

multi_val(true, true, maybe<bool>(), maybe<bool>)
</bool></bool>

Это попадает в довольно пьянящую территорию С++ и, определенно, не то, что я сделал бы для чего-то столь элементарного.


Мне нравится решение Алана, но я почтительно не согласен с его заключением, что он слишком сложный. Если у вас есть доступ к С++ 11, он дает вам практически все необходимые инструменты. Вам нужно написать только один класс и две функции:

namespace always {
struct always_eq_t {
};
template <class lhs_t="">
bool operator==(lhs_t const&, always_eq_t)
{
 return true;
}
template <class rhs_t="">
bool operator==(always_eq_t, rhs_t const&)
{
 return true;
}
} // always
</class></class>

Затем вы можете написать свою функцию так же, как ML:

#include <tuple>
#include <iostream>
void f(bool a, bool b, bool c, bool d)
{
 always::always_eq_t _;
 auto abcd = std::make_tuple(a, b, c, d);
 if (abcd == std::make_tuple(true, true, _, _)) {
 std::cout << "true, true, _, _\n";
 } else if (abcd == std::make_tuple(false, true, _, false)) {
 std::cout << "false, true, _, false\n";
 } else {
 std::cout << "else\n";
 }
}
int
main()
{
 f(true, true, true, true);
 f(false, true, true, false);
 return 0;
}
</iostream></tuple>

В С++ вы часто хотите рассмотреть, есть ли разумный тип, который я могу создать, который поможет мне легче написать мой код? Кроме того, я думаю, что если у вас есть опыт работы в ML, вы сможете многому выиграть от изучения шаблонов на С++. Они очень полезны при применении стиля функционального программирования в С++.


Для С++ 11, предполагая, что вы хотите только соответствовать фиксированному количеству логических элементов и можете жить без сопоставления с шаблоном, тогда [1] (развернуть число требуемых переменных).

Я все еще работаю над альтернативным решением, используя шаблоны для сопоставления произвольных типов с использованием lambdas или функторов для выражений.

-Edit -

Как и было обещано, [2] сопоставление образцов произвольных типов, в т.ч. неопределенные значения.

Обратите внимание на пару оговорок:

  • Этот код работает только с 4 переменными (на самом деле мой первый набег на метапрограммирование шаблонов). Это можно было бы значительно улучшить с помощью вариативных шаблонов.
  • Это работает, но это не очень аккуратно или хорошо организовано. Более доказательство концепции, которая должна быть очищена до введения в производственный код.
  • Я не доволен функцией соответствия. Я надеялся использовать списки инициализаторов для передачи выражений, которые будут оцениваться, и остановки в первом матче (с текущей реализацией каждое условие соответствия будет выполнено) - однако я не мог быстро подумать о том, как передавать выражения, соответствующие объектам разных типов через единственный список инициализаторов.

Я не могу придумать метод для проверки того, что таблица истинности является исчерпывающей.

Приветствия,

-nick

[1]

constexpr int match(bool v, int c)
{
 return v ? (1 << c) : 0;
}
constexpr int match(bool a, bool b)
{
 return match(a, 0) | match(b, 1);
}
int main()
{
 int a = true;
 int b = false;
 switch(match(a, b))
 {
 case match(false, false):
 break;
 case match(false, true):
 break;
 case match(true, false):
 break;
 case match(true, true):
 break;
 }
}

[2]

template<typename v1,="" typename="" v2,="" v3,="" v4="">
class pattern_match_t
{
private:
 V1 value_0;
 V2 value_1;
 V3 value_2;
 V4 value_3;
public:
 typedef std::function<void(v1, v2,="" v3,="" v4)=""> expr_fn;
 template <typename c1,="" typename="" c2,="" c3,="" c4="">
 pattern_match_t<v1, v2,="" v3,="" v4="">& match(C1 a, C2 b, C3 c, C4 d, expr_fn fn)
 {
 if(value_0 == a && value_1 == b && value_2 == c && value_3 == d)
 fn(value_0, value_1, value_2, value_3);
 return *this;
 }
 pattern_match_t(V1 a, V2 b, V3 c, V4 d)
 : value_0(a), value_1(b), value_2(c), value_3(d)
 {
 }
};
template<typename t="">
class unspecified
{};
template<typename t="">
constexpr bool operator==(unspecified<t>, const T&)
{
 return true;
}
template<typename t="">
constexpr bool operator==(const T&, unspecified<t>)
{
 return true;
}
template<typename v1,="" typename="" v2,="" v3,="" v4="">
pattern_match_t<v1, v2,="" v3,="" v4=""> pattern_match(V1 a, V2 b, V3 c, V4 d)
{
 return pattern_match_t<v1, v2,="" v3,="" v4="">(a, b, c, d);
}
int main()
{
 bool test_a = true;
 std::string test_b = "some value";
 bool test_c = false;
 bool test_d = true;
 pattern_match(test_a, test_b, test_c, test_d)
 .match(true, unspecified<std::string>(), false, true, [](bool, std::string, bool, bool)
 {
 return;
 })
 .match(true, "some value", false, true, [](bool, std::string, bool, bool)
 {
 return;
 });
}
</std::string></v1,></v1,></typename></t></typename></t></typename></typename></v1,></typename></void(v1,></typename>

licensed under cc by-sa 3.0 with attribution.