Синтаксический сахар: x in (a,b,c)
От: MarcoPolo  
Дата: 18.02.20 08:59
Оценка:
Уважаемые коллеги,

Можно ли в C++ 14 как-нибудь (коротко) выразить конструкцию, аналогичную питоновской:


if x in (1,2,4,6,7):
  print('x is either 1,2,4,6 or 7')
else:
  print('x is something else')


Понятное, что можно сделать вектор, потом воспользоваться find_if и т.д. Но это уже несколько строк, а хотелось бы in-place.

Заранее спасибо!
c++ find value in collection inplace
Re: Синтаксический сахар: x in (a,b,c)
От: GhostCoders Россия  
Дата: 18.02.20 09:02
Оценка:
Здравствуйте, MarcoPolo, Вы писали:

MP>Можно ли в C++ 14 как-нибудь (коротко) выразить конструкцию, аналогичную питоновской:

написать шаблонную функцию in от переменного числа аргументов?
Третий Рим должен пасть!
Re[2]: Синтаксический сахар: x in (a,b,c)
От: GhostCoders Россия  
Дата: 18.02.20 09:07
Оценка: 7 (2)
Здравствуйте, GhostCoders, Вы писали:

GC>написать шаблонную функцию in от переменного числа аргументов?


#include <iostream>
using namespace std;

template<typename T>
bool in(T value, T last)
{
    if (value == last)
        return true;
    return false;
}

template<typename T, typename... Args>
bool in(T value, T first, Args... args)
{
    if (value == first)
        return true;
    return in(value, args...);
}

int main()
{
    if (in(5, 1, 2, 4, 6, 7))
        cout << "5 in 1, 2, 4, 6, 7" << endl;
    else
        cout << "5 not in 1, 2, 4, 6, 7" << endl;
    if (in(4, 1, 2, 4, 6, 7))
        cout << "4 in 1, 2, 4, 6, 7" << endl;        
    else
        cout << "4 not in 1, 2, 4, 6, 7" << endl;        
    return 0;
}
Третий Рим должен пасть!
Отредактировано 18.02.2020 9:10 GhostCoders . Предыдущая версия . Еще …
Отредактировано 18.02.2020 9:09 GhostCoders . Предыдущая версия .
Re: Синтаксический сахар: x in (a,b,c)
От: rg45 СССР  
Дата: 18.02.20 09:17
Оценка: 7 (2)
Здравствуйте, MarcoPolo, Вы писали:

MP>Уважаемые коллеги,


MP>Можно ли в C++ 14 как-нибудь (коротко) выразить конструкцию, аналогичную питоновской:


MP>

MP>if x in (1,2,4,6,7):
MP>  print('x is either 1,2,4,6 or 7')
MP>else:
MP>  print('x is something else')

MP>


В виде обычной вариадик-функции можно: http://coliru.stacked-crooked.com/a/8bb6c54baf9352b9:

template <typename T, typename...U>
bool in(T&& t, U&&...u) {
    return ((t == u) || ...);
}
--
Re[3]: Синтаксический сахар: x in (a,b,c)
От: MarcoPolo  
Дата: 18.02.20 09:18
Оценка:
Независимо от вас написал

template<typename T>
bool in_list(T x, T v) {
  return x == v;
}

template<typename T, typename... Args>
bool in_list(T x, T first, Args... args) {
  return x == first || in_list(x, args...);
}
Re: Синтаксический сахар: x in (a,b,c)
От: jahr  
Дата: 18.02.20 09:19
Оценка:
Здравствуйте, MarcoPolo, Вы писали:

Прошу прощения, затупил, не про то ответил.)
Отредактировано 18.02.2020 9:22 jahr . Предыдущая версия .
Re: Синтаксический сахар: x in (a,b,c)
От: jahr  
Дата: 18.02.20 09:30
Оценка: 13 (3) +2 -1
Здравствуйте, MarcoPolo, Вы писали:

О, можно так:

if std::set({1,2,4,6,7}).count(x)
    std::cout << "x is either 1,2,4,6 or 7" << std::endl;
Re[2]: Синтаксический сахар: x in (a,b,c)
От: flаt  
Дата: 18.02.20 09:36
Оценка: +1
Здравствуйте, rg45, Вы писали:


R>В виде обычной вариадик-функции можно: http://coliru.stacked-crooked.com/a/8bb6c54baf9352b9:


R>
R>template <typename T, typename...U>
R>bool in(T&& t, U&&...u) {
R>    return ((t == u) || ...);
R>}
R>



Fold expressions от C++17, а ТС обозначил 14й стандарт.
Re[3]: Синтаксический сахар: x in (a,b,c)
От: rg45 СССР  
Дата: 18.02.20 09:45
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>
GC>    if (value == last)
GC>        return true;
GC>    return false;
GC>


Почему бы не написать просто:

    return value == last;
--
Re: Синтаксический сахар: x in (a,b,c)
От: K13 http://akvis.com
Дата: 18.02.20 09:50
Оценка:
Здравствуйте, MarcoPolo, Вы писали:

MP>Уважаемые коллеги,


MP>Можно ли в C++ 14 как-нибудь (коротко) выразить конструкцию, аналогичную питоновской:


MP>[python]


MP>if x in (1,2,4,6,7):


а определение функции в хидер сунуть можно?

template<typename T>
bool in_list( const T& needle, const std::initializer_list<T>& haystack ) {
  return std::find( haystack.begin(), haystack.end(), needle ) != haystack.end();
}

if ( in_list( k, {3,6,some_var,foo()} ) {
  // ...
} else {
  // ...
}
Re[4]: Синтаксический сахар: x in (a,b,c)
От: rg45 СССР  
Дата: 18.02.20 10:02
Оценка:
Здравствуйте, MarcoPolo, Вы писали:

MP>Независимо от вас написал

MP>
MP>template<typename T>
MP>bool in_list(T x, T v) {
MP>  return x == v;
MP>}

MP>template<typename T, typename... Args>
MP>bool in_list(T x, T first, Args... args) {
MP>  return x == first || in_list(x, args...);
MP>}
MP>


Для того, чтобы поддержать пустые списки, лучше написать чуть по-другому:

template<typename T>
bool in_list(T&&) {
  return false;
}

template<typename T, typename... Args>
bool in_list(T&& x, T&& first, Args&&... args) {
  return x == first || in_list(x, args...);
}


Ну и параметры лучше принимать по ссылкам, все-таки — чтобы работало и для некопируемых/неперемещаемых типов.
--
Re: Синтаксический сахар: x in (a,b,c)
От: Zhendos  
Дата: 18.02.20 10:17
Оценка: 3 (1) +1
Здравствуйте, MarcoPolo, Вы писали:

MP>Уважаемые коллеги,


MP>Можно ли в C++ 14 как-нибудь (коротко) выразить конструкцию, аналогичную питоновской:


MP>

MP>if x in (1,2,4,6,7):
MP>  print('x is either 1,2,4,6 or 7')
MP>else:
MP>  print('x is something else')

MP>


есть целая лекция по этому поводу,
там сравниваются разные подходы и даже смотрят получившийся
ассемблер чтобы убедиться что генерируется нормальный код:

https://youtu.be/XBUXBYpEedI
Re: Синтаксический сахар: x in (a,b,c)
От: PM  
Дата: 18.02.20 20:04
Оценка: 33 (8)
Здравствуйте, MarcoPolo, Вы писали:

MP>Можно ли в C++ 14 как-нибудь (коротко) выразить конструкцию, аналогичную питоновской:


MP>

MP>if x in (1,2,4,6,7):
MP>  print('x is either 1,2,4,6 or 7')
MP>else:
MP>  print('x is something else')

MP>


MP>Понятное, что можно сделать вектор, потом воспользоваться find_if и т.д. Но это уже несколько строк, а хотелось бы in-place.


Можно. Нужно только сделать бинарный оператор, аналогичный питоновскому in:
#include <algorithm>
#include <vector>
#include <array>
#include <iostream>

const struct in_op final
{
    template<typename T>
    struct impl
    {
        T const& x;

        template<typename Container>
        bool operator>(Container const& c) const
        {
            return std::find(std::begin(c), std::end(c), x) != std::end(c);
        }
    };

    template<typename T>
    friend auto operator<(T const &x, in_op)
    {
        return impl<T>{x};
    }
} in;

int main()
{
    const std::array<int, 3>         a{ 1, 2, 3 };
    const std::vector<int>           b{ 1, 2, 3 };
    const std::initializer_list<int> c{ 5, 6, 7 };
    
    std::cout << "5 in { 1, 2, 3 } = " << std::boolalpha << (5 <in> a) << '\n';
    std::cout << "2 in { 1, 2, 3 } = " << std::boolalpha << (2 <in> b) << '\n';
    std::cout << "7 in { 5, 6, 7 } = " << std::boolalpha << (7 <in> c) << '\n';
    return 0;
}


Результат запуска https://ideone.com/wnpoqL

5 in { 1, 2, 3 } = false
2 in { 1, 2, 3 } = true
7 in { 5, 6, 7 } = true

Re: Синтаксический сахар: x in (a,b,c)
От: B0FEE664  
Дата: 18.02.20 22:47
Оценка:
Здравствуйте, MarcoPolo, Вы писали:

MP>Можно ли в C++ 14 как-нибудь (коротко) выразить конструкцию, аналогичную питоновской:

MP>

MP>if x in (1,2,4,6,7):
MP>  print('x is either 1,2,4,6 or 7')
MP>else:
MP>  print('x is something else')

MP>


MP>Понятное, что можно сделать вектор, потом воспользоваться find_if и т.д. Но это уже несколько строк, а хотелось бы in-place.


Зачем несколько строк? Пишем по месту:
#include <iostream>
#include <initializer_list>
#include <algorithm>

int main() {
    
    int x = 7;
    
    if (const auto a = {1,2,4,6,7}; a.end() != std::find(a.begin(), a.end(), x) )
      std::cout << "x is either 1,2,4,6 or 7" << std::endl;
    else
      std::cout << "x is something else" << std::endl;
    
    // your code goes here
    return 0;
}


https://ideone.com/7qBtBS

А вообще есть ещё такое:
for(auto n : {1,2,4,6,7})
  if ( x == n )
  {
    std::cout << "x is either 1,2,4,6 or 7" << std::endl;
    break;
  }



Думаю этот if в C++20 можно будет записать действительно коротко.
И каждый день — без права на ошибку...
Re[2]: Синтаксический сахар: x in (a,b,c)
От: rg45 СССР  
Дата: 19.02.20 09:42
Оценка:
Здравствуйте, jahr, Вы писали:

J>О, можно так:

J>

J>if std::set({1,2,4,6,7}).count(x)
J>    std::cout << "x is either 1,2,4,6 or 7" << std::endl;

J>


Поиск по списку с полным копированием списка? Причем с двойным копированием — сначала в initializer_list, затем в set — в случае если элементы списка заданы не prvalue выражениями. Ну, для простых типов, наверное, приемлемо.
--
Re[3]: Синтаксический сахар: x in (a,b,c)
От: Аноним  
Дата: 19.02.20 14:17
Оценка:
Здравствуйте, rg45, Вы писали:

J>>

J>>if std::set({1,2,4,6,7}).count(x)
J>>    std::cout << "x is either 1,2,4,6 or 7" << std::endl;

J>>


R>Поиск по списку с полным копированием списка? Причем с двойным копированием — сначала в initializer_list, затем в set — в случае если элементы списка заданы не prvalue выражениями. Ну, для простых типов, наверное, приемлемо.


Есть вариант без двойного копирования, но с бустом:
if (boost::algorithm::any_of_equal(std::initializer_list<int>{1,2,4,6,7}, x))
    std::cout << "x is either 1,2,4,6 or 7" << std::endl;
Re[3]: Синтаксический сахар: x in (a,b,c)
От: jahr  
Дата: 19.02.20 14:24
Оценка:
Здравствуйте, rg45, Вы писали:

R>Поиск по списку с полным копированием списка? Причем с двойным копированием — сначала в initializer_list, затем в set — в случае если элементы списка заданы не prvalue выражениями. Ну, для простых типов, наверное, приемлемо.


Это, конечно, как раз для тривиальных случаев типа описанного в вопросе, там где хоть 10 раз скопируй все туда-сюда — на производительности это не особо скажется.)
Re[4]: Синтаксический сахар: x in (a,b,c)
От: rg45 СССР  
Дата: 19.02.20 14:32
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Есть вариант без двойного копирования, но с бустом:

А>
А>if (boost::algorithm::any_of_equal(std::initializer_list<int>{1,2,4,6,7}, x))
А>    std::cout << "x is either 1,2,4,6 or 7" << std::endl;
А>


Заменяем "{1,2,3,4,6,7}" на "{a,b,c,d,e}" и все равно получаем копирование.

Да и назвать такое выражение "синтаксическическим сахаром" как-то язык не поворачивается. Лично мне больше по душе "in_list(x, a, b, c, d, e)".
--
Отредактировано 19.02.2020 14:33 rg45 . Предыдущая версия .
Re[5]: Синтаксический сахар: x in (a,b,c)
От: ομικρον  
Дата: 19.02.20 15:01
Оценка:
Здравствуйте, rg45, Вы писали:

А>>
А>>if (boost::algorithm::any_of_equal(std::initializer_list<int>{1,2,4,6,7}, x))
А>>    std::cout << "x is either 1,2,4,6 or 7" << std::endl;
А>>


R>Заменяем "{1,2,3,4,6,7}" на "{a,b,c,d,e}" и все равно получаем копирование.


Это копирование и имелось в виду. Нет второго копирования в std::set.

R>Да и назвать такое выражение "синтаксическическим сахаром" как-то язык не поворачивается. Лично мне больше по душе "in_list(x, a, b, c, d, e)".


Согласен: в такой конструкции много букв.
Re: ranges ?
От: B0FEE664  
Дата: 20.02.20 12:50
Оценка:
Уважаемые коллеги, а кто-нибудь может показать решение этого вопроса с помощью ranges?
Коротко выразить конструкцию, аналогичную питоновской:
MP>
MP>if x in (1,2,4,6,7):
MP>  print('x is either 1,2,4,6 or 7')
MP>else:
MP>  print('x is something else')
MP>
И каждый день — без права на ошибку...
Re[2]: ranges ?
От: Chorkov Россия  
Дата: 20.02.20 16:07
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Уважаемые коллеги, а кто-нибудь может показать решение этого вопроса с помощью ranges?


Ranges немного для другого.
С их помощью можно, например, улучшить сообшщения об ошибках. (Получать их в точке инстационирования шаблонной функции, а не в ее внутренностях.)
Но если постараться, то можно и так...

#include <iostream>
#include <vector>
#include <array>
#include <ranges>
#include <algorithm>
#include <string>
#include <concepts>

using namespace std::literals::string_literals;

// concepts-style
template<typename T, std::ranges::range Container>
     requires requires (const T& value,  const Container& cnt) { value==*std::begin(cnt); }  // Можно как-то по проще написать?
bool in_range( const T& value,  const Container& cnt )
{
    return std::ranges::find( cnt, value ) != std::ranges::end( cnt );
}



// old-style
template<typename T, typename Container>
auto in_any_container( const T& value,  const Container& cnt )
    -> decltype( value== *std::begin(cnt) ) // hand-make constrait!
{
    for( auto& v : cnt )
        if(v==value)
            return true;
    return false;
}


int main()
{
// Success cases:
    std::vector<int> vect{1,2,3};
    std::cout<< in_range(1, vect ) <<std::endl;
    std::cout<< in_any_container(1, vect ) <<std::endl;
    std::cout<< std::ranges::count( vect, 1 ) <<std::endl;
    
    
    int c_array[]={1,2,3};
    std::cout<< in_range(1, c_array ) <<std::endl;
    std::cout<< in_any_container(1, c_array ) <<std::endl;
    std::cout<< std::ranges::count( c_array, 1 ) <<std::endl;
    
    
    std::cout<< in_range(1, std::initializer_list{1,2,3} ) <<std::endl;
    std::cout<< in_any_container(1, std::initializer_list{1,2,3} ) <<std::endl;
    std::cout<< std::ranges::count( std::initializer_list{1,2,3}, 1 ) <<std::endl;
    
    std::cout<< in_range(1,         std::array{1,2,3} ) <<std::endl;
    std::cout<< in_any_container(1, std::array{1,2,3} ) <<std::endl;
    std::cout<< std::ranges::count( std::array{1,2,3}, 1 ) <<std::endl;
    
// Error cases:

    std::cout<< in_range("1"s,         vect       ) <<std::endl;
/*
prog.cc: In function 'int main()':
prog.cc:58:51: error: no matching function for call to 'in_range(std::__cxx11::basic_string<char>, std::vector<int>&)'
   58 |     std::cout<< in_range("1"s,         vect       ) <<std::endl;
      |                                                   ^
prog.cc:14:6: note: candidate: 'bool in_range(const T&, const Container&) [with T = std::__cxx11::basic_string<char>; Container = std::vector<int>]'
   14 | bool in_range( const T& value,  const Container& cnt )
      |      ^~~~~~~~    
...
*/

    std::cout<< in_any_container("1"s, vect       ) <<std::endl;
/*
prog.cc: In function 'int main()':
prog.cc:59:51: error: no matching function for call to 'in_any_container(std::__cxx11::basic_string<char>, std::vector<int>&)'
   59 |     std::cout<< in_any_container("1"s, vect       ) <<std::endl;
      |                                                   ^
prog.cc:23:6: note: candidate: 'template<class T, class Container> decltype ((value == (* std::begin(cnt)))) in_any_container(const T&, const Container&)'
   23 | auto in_any_container( const T& value,  const Container& cnt )
      |      ^~~~~~~~~~~~~~~~
...
*/
    std::cout<< std::ranges::count(    vect, "1"s ) <<std::endl;
/*
prog.cc: In function 'int main()':
prog.cc:60:51: error: no match for call to '(const std::ranges::__count_fn) (std::vector<int>&, std::__cxx11::basic_string<char>)'
   60 |     std::cout<< std::ranges::count(    vect, "1"s ) <<std::endl;
      |                                                   ^
In file included from /opt/wandbox/gcc-head/include/c++/10.0.1/algorithm:64,
                 from prog.cc:5:
/opt/wandbox/gcc-head/include/c++/10.0.1/bits/ranges_algo.h:369:7: note: candidate: 'template<class _Iter, class _Sent, class _Tp, class _Proj>  requires (input_iterator<_Iter>) && (sentinel_for<_Sent, _Iter>) && (indirect_binary_predicate<std::ranges::equal_to, std::projected<_I1, _P1>, const _Tp*>) constexpr std::iter_difference_t<_Iter> std::ranges::__count_fn::operator()(_Iter, _Sent, const _Tp&, _Proj) const'
  369 |       operator()(_Iter __first, _Sent __last,
      |       ^~~~~~~~
*/

    return 0;
}
Re[3]: ranges ?
От: B0FEE664  
Дата: 20.02.20 16:30
Оценка:
Здравствуйте, Chorkov, Вы писали:

BFE>>Уважаемые коллеги, а кто-нибудь может показать решение этого вопроса с помощью ranges?

C>Ranges немного для другого.

Странно. Я ожидал чего-то такого:
if ( {1,2,4,6,7} | std::ranges::count(x) )
  ....

или
if ( ({1,2,4,6,7} | std::views::filter([x](int i){ return x == i; })).size() )
  ....

так или как-то так нельзя?
И каждый день — без права на ошибку...
Re[4]: ranges ?
От: rg45 СССР  
Дата: 20.02.20 19:22
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Странно. Я ожидал чего-то такого:

BFE>
BFE>if ( {1,2,4,6,7} | std::ranges::count(x) )
BFE>  ....
BFE>

BFE>или
BFE>
BFE>if ( ({1,2,4,6,7} | std::views::filter([x](int i){ return x == i; })).size() )
BFE>  ....
BFE>


Это для того, чтобы дотнетчики приходили пальцами потыкать?
--
Re[3]: Синтаксический сахар: x in (a,b,c)
От: koenjihyakkei Россия  
Дата: 20.02.20 20:57
Оценка:
Здравствуйте, GhostCoders, Вы писали:

В С++17 со сверткой можно проще:

#include <iostream>
using namespace std;

template<typename T, typename... Args>
bool in(T value, Args... args)
{
    return ((value == args) || ...);
}

int main()
{
    if (in(5, 1, 2, 4, 6, 7))
        cout << "5 in 1, 2, 4, 6, 7" << endl;
    else
        cout << "5 not in 1, 2, 4, 6, 7" << endl;
    if (in(4, 1, 2, 4, 6, 7))
        cout << "4 in 1, 2, 4, 6, 7" << endl;        
    else
        cout << "4 not in 1, 2, 4, 6, 7" << endl;        
    return 0;
}
Re[2]: ranges ?
От: Voivoid Россия  
Дата: 21.02.20 07:00
Оценка: 12 (2)
Здравствуйте, B0FEE664, Вы писали:

BFE>Уважаемые коллеги, а кто-нибудь может показать решение этого вопроса с помощью ranges?


https://godbolt.org/z/rzD67Z

#include "range/v3/algorithm/contains.hpp"
#include <array>

int main() {
  if( ranges::contains(std::array{1,2,3,4,5,6}, 42) ) {
      return 0;
  }
  return 1;
}
Re[3]: ranges ?
От: andyp  
Дата: 21.02.20 08:59
Оценка:
Здравствуйте, Voivoid, Вы писали:

V>
V>#include "range/v3/algorithm/contains.hpp"
V>#include <array>

V>int main() {
V>  if( ranges::contains(std::array{1,2,3,4,5,6}, 42) ) {
V>      return 0;
V>  }
V>  return 1;
V>}

V>


Порадовало, что это компилятор до return 1 смог оптимизировать.
Re[4]: ranges ?
От: rg45 СССР  
Дата: 21.02.20 09:55
Оценка:
Здравствуйте, andyp, Вы писали:

A>Порадовало, что это компилятор до return 1 смог оптимизировать.


Вот так почестнее будет:

https://godbolt.org/z/irQRU9

int main() {
    int x {};
    std::cin >> x;
    if( ranges::contains(std::array{1,2,3,4,5,6}, x) ) {
        return 0;
    }
    return 1;
}



P.S. Ну и для сравнения:

https://godbolt.org/z/2nTHxD

template <typename T, typename...U>
bool in_list(T&& t, U&&...u) {
    return ((t == u) || ...);
}

int main() {
    int x {};
    std::cin >> x;
    if( in_list(x,1,2,3,4,5,6) ) {
        return 0;
    }
    return 1;
}
--
Отредактировано 21.02.2020 10:07 rg45 . Предыдущая версия .
Re[5]: ranges ?
От: andyp  
Дата: 21.02.20 10:21
Оценка: +1 :)
Здравствуйте, rg45, Вы писали:


R>P.S. Ну и для сравнения:


Тоже читерство. Циферки подряд Но, кстати, работа оптимизатора и тут впечатляет.
Re[3]: ranges ?
От: B0FEE664  
Дата: 21.02.20 12:40
Оценка:
Здравствуйте, Voivoid, Вы писали:

BFE>>Уважаемые коллеги, а кто-нибудь может показать решение этого вопроса с помощью ranges?

V>https://godbolt.org/z/rzD67Z

V>
V>#include "range/v3/algorithm/contains.hpp"
V>#include <array>

V>int main() {
V>  if( ranges::contains(std::array{1,2,3,4,5,6}, 42) ) {
V>      return 0;
V>  }
V>  return 1;
V>}

V>


Не понятно зачем нужен std::array, должно ведь и так работать: ranges::contains({1,2,3,4,5,6}, 42). Или нет?
И каждый день — без права на ошибку...
Re[4]: ranges ?
От: rg45 СССР  
Дата: 21.02.20 13:02
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Не понятно зачем нужен std::array, должно ведь и так работать: ranges::contains({1,2,3,4,5,6}, 42). Или нет?


Это зависит от того, как объявлен параметр. Если просто T&& или const T&, то не выведется.
--
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.