Существует ли способ определить переменные двух различных типов в для инициализатора цикла?

Здесь:

for row in df.itertuples():
    print(row.num_legs)
  # print(row.num_wings)   # Other column values

# Output
4
2
6
задан Mechanical snail 17 August 2012 в 04:20
поделиться

13 ответов

Вот версия, использующая препроцессор ускорения (это просто для удовольствия. Реальный ответ см. Выше в ответе на @ kitchen):

FOR((int i = 0)(int j = 0.0), i < 10, (i += 1, j = 2 * i)) { 

}

Первая часть определяет последовательность объявлений: (а) (б) ... . Объявленные позже переменные могут ссылаться на переменные, объявленные перед ними. Вторая и третья часть как обычно. Если во второй и третьей частях встречаются запятые, можно использовать круглые скобки, чтобы они не разделяли аргументы макроса.

Есть два известных мне трюка, которые используются для объявления переменных, которые позже становятся видимыми в составном операторе, добавленном вне макроса. Первый использует условия, например if:

if(int k = 0) ; else COMPOUND_STATEMENT

Then k видим. Естественно, он всегда должен возвращать значение false . Поэтому мы не можем его использовать. Другой контекст такой:

for(int k = 0; ...; ...) COMPOUND_STATEMENT

Вот что я ' м собираюсь использовать здесь. Придется следить, чтобы выполнить только одну итерацию COMPOUND_STATEMENT . Фактический цикл for , который выполняет приращение и проверку условий, должен идти в конце, поэтому добавленный составной оператор относится к нему.

#include <boost/preprocessor.hpp>
#include <iostream>

#define EMIT_DEC_(R,D,DEC) \
    for(DEC; !_k; ) 

#define FOR(DECS, COND, INC) \
    if(bool _k = false) ; else \
      BOOST_PP_SEQ_FOR_EACH(EMIT_DEC_, DECS, DECS) \
        for(_k = true; COND; INC)

int main() {
    FOR((int i = 0)(float j = 0.0f), i < 10, (i += 1, j = 2 * i)) {
        std::cout << j << std::endl;
    }
}

Он создает группу из for операторов, каждый вложен в другой. Он расширяется до:

if(bool _k = false) ; else
  for(int i = 0; !_k; )
    for(float j = 0.0f; !_k; )
      for(_k = true; i < 10; (i += 1, j = 2 * i)) {
        std::cout << j << std::endl;
      }
10
ответ дан 8 December 2019 в 02:00
поделиться

Вы говорите, что i - это ваш собственный тип, и вам просто нужно сгенерировать j из i , верно? Легко. Добавьте функцию-член в класс i , чтобы сгенерировать значение j , и используйте его всегда. Возможно, вы даже можете создать макрос, чтобы «скрыть» вызов этой функции-члена, если хотите. : -)

1
ответ дан 8 December 2019 в 02:00
поделиться

Почему бы вам просто не объявить и не инициализировать свои переменные вне цикла for? Вы все еще можете тестировать и увеличивать, как и сейчас.

0
ответ дан 8 December 2019 в 02:00
поделиться
int main() {
  for (int i = 0, float j = 0.0; i < 10; i += 1, j = 2*i) {
    cout << j << endl;
  }
}

Может быть, я тупой, но зачем вам вообще объявлять float? Вы просто выбрасываете его, когда выходите из цикла. Верно?

for(int i=0; i<10; ++i)
    cout << (float)2*i << endl;

Зачем вам j?

1
ответ дан 8 December 2019 в 02:00
поделиться

С учетом требований, которые вы даете, самый простой код, который я могу придумать, такой:

for ( int i = 0; i < 10; ++i )
{
   float f = i * 2;
   std::cout << f << std::endl;
}

Вы используете f только как удвоенное значение i . Время жизни ограничено циклом, и (по крайней мере, в упрощенном вопросе, который вы задаете) создание поплавков дешево (точно так же дешево, как и назначать).

Если построение реального поплавка (я если предположить, что, поскольку i на самом деле не является int, f тоже может не быть float) намного дороже, чем переназначение значения, тогда другие решения инкапсуляции внутри дополнительной пары фигурных скобок для ограничения области видимости будут лучшим вариантом.

2
ответ дан 8 December 2019 в 02:00
поделиться

РЕДАКТИРОВАТЬ : Вопрос снова изменился. Теперь вопрос явно хочет реализовать цикл foreach. Самый простой ответ:

#include <boost/foreach.hpp>
void( std::vector<int>& v ) {
   BOOST_FOREACH( int & x, v ) {
      x = x*2;
   }
}

Внедрение переменной в кодовый блок

Это не предназначено как ответ, но чтобы показать более общую технику для введения переменной в кодовый блок. Кажется, что макрос, который OP пытается определить, мог бы использовать, даже если он влечет за собой некоторые накладные расходы

Есть несколько мест, где вы можете определить переменную с различными областями действия. Вы можете определить переменную внутри любого блока кода, и ее срок службы будет до конца этого конкретного блока. Вы можете определить переменную в скобках цикла for, и областью действия будет блок цикла. Вы также можете определить переменную внутри блока if, и ее область действия будет такой же, как у if (включая предложение else).

Вы можете комбинировать эти параметры, указанные выше, для внешнего создания и вставки переменных в блок кода без создания переменной, чей срок службы превышает срок службы блока. Практическим примером может быть определение цикла foreach (упрощенного для работы только с контейнерами STL. Синтаксис вызова будет следующим:

void f( std::vector<int>& container ) 
{
   INTVECTOR_FOREACH( int & x, container )
   {
      x = x*2;
   }
}

С семантикой, аналогичной foreach в других языках: x ссылается на каждый элемент в контейнере, так что функция фактически удваивает каждое значение внутри целочисленного вектора.

Теперь код упрощенного макроса:

#define INTVECTOR_FOREACH( variable, container ) \
   for ( std::vector<int>::iterator it = container.begin(); it!=container.end(); ++it ) \
      if ( bool condition=false ) {} else \
         for ( variable = *it; !condition; condition=true )

Обобщение макроса для любого контейнера и типа требует некоторого метапрограммирования, которое выпадает из контекста вопроса, но идея того, как он работает (я надеюсь), не должна быть слишком сложной для понимания.

Внешний ] for выполняет итерацию по контейнеру, на каждой итерации мы выполняем другой для только один раз, определяя переменную итерации (int & x в примере кода). Нам нужно условие для управления количеством итераций (1) внутреннего цикла, и это условие вводится с if . Мы решили сделать if неудачным, чтобы мы могли гарантировать, что пользователь не получит неожиданных результатов, если он напишет else после цикла ... макросы сложны.

Внешний для выполняет итерацию по контейнеру, на каждой итерации мы выполняем другой для только один раз, определяя повторяющуюся переменную (int & x в примере кода). Нам нужно условие для управления количеством итераций (1) внутреннего цикла, и это условие вводится с if . Мы решили сделать if неудачным, чтобы мы могли гарантировать, что пользователь не получит неожиданных результатов, если он напишет else после цикла ... макросы сложны.

Внешний для выполняет итерацию по контейнеру, на каждой итерации мы выполняем другой для только один раз, определяя повторяющуюся переменную (int & x в примере кода). Нам нужно условие для управления количеством итераций (1) внутреннего цикла, и это условие вводится с if . Мы решили сделать if неудачным, чтобы мы могли гарантировать, что пользователь не получит неожиданных результатов, если он напишет else после цикла ... макросы сложны.

4
ответ дан 8 December 2019 в 02:00
поделиться

Это заставит итератор (или, в данном случае, float) исчезнуть, когда он больше не нужен:

int main() {
  // some code...

  {
    float j = 0.0;
    for (int i = 0; i < 10; i += 1, j = 2*i) {
      cout << j << endl;
    }
  }

  // more code...
}
7
ответ дан 8 December 2019 в 02:00
поделиться

Если у вас проблемы с макросами, есть стандартный трюк do.. while , который отлично работает:

#define MYFOR(init, test, post, body) \
    do \
    { \
        init \
        for( ; test; post) \
            body \
    } while(0)

Используйте его следующим образом:

MYFOR( int i = 0; float j = 0.0f; , i < 10 , (i += 1, j = 2.0f * i),
    {
         cout << j << endl;
    } );

Это уродливо , но он делает то, что вы хотите: область действия i и j ограничена циклом do.. while из макроса, и для него требуется точка с запятой в конце, чтобы вас не укусили, поместив его в предикат оператора if / else.

6
ответ дан 8 December 2019 в 02:00
поделиться

Пожалуйста, не предлагайте перемещать какие-либо переменные за пределы for body, возможно, это не подходит для меня, поскольку итератор должен исчезнуть сразу после цикла.

Вы все еще можете сделать это и заключить все это в фигурные скобки, чтобы лишняя переменная вышла из области видимости.

int main() 
{
  {
    float j = 0.0;
    for (int i = 0; i < 10; i += 1, j = 2*i) 
    {
      cout << j << endl;
    }
  }
  // more code...
}

Таким образом j выйдет из области видимости сразу после цикла.

3
ответ дан 8 December 2019 в 02:00
поделиться
{
  int i = 0;
  float j = 0.0;
  for ( ; i < 10; i += 1, j = 2*i) {
    cout << j << endl;
  }
}

Переменные "исчезают" после блока.

8
ответ дан 8 December 2019 в 02:00
поделиться

Ну, это уродливо. Но вы можете использовать пару.

int main() {
  for (std::pair<int,float> p(0,0.0f); 
       p.first < 10; 
       p.first += 1, p.second = 2*p.first) {
    cout << p.second << endl;
  }
}
12
ответ дан 8 December 2019 в 02:00
поделиться

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

Вы можете сделать это:

#include <iostream>

int main( int, char *[] ) {
    {
        float j = 0.0;

        for ( int i = 0; i < 10; i += 1, j = 2*i ) {
            std::cout << j << std::endl;
        }
    }

    float j = 2.0; // works

    std::cout << j << std::endl;

    return 0;
}
25
ответ дан 8 December 2019 в 02:00
поделиться

Этот вариант также уродлив, но предоставляет также некоторый общий способ объявления нескольких переменных с заданным именем и типами в цикле for

int main() {
  for (struct { int i; float j; } x = { };
       x.i < 10; x.i += 1, x.j = 2 * x.i) {
    cout << x.j << endl;
  }
}
5
ответ дан 8 December 2019 в 02:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: