Хотя синтаксис выражений типа a = a++
или a++ + a++
является законным, поведение этих конструкций не определено, потому что должно в стандарте C не соблюдаться. C99 6.5p2 :
blockquote>
- Между предыдущей и следующей точкой последовательности объект должен иметь неизменяемое значение хранимого значения не более одного раза путем оценки выражения. [72] Кроме того, предыдущее значение должно быть считано только для определения сохраняемого значения [73]
С помощью сноски 73 далее уточняется, что
blockquote>
- Этот абзац отображает неопределенные выражения операторов, такие как
, позволяяi = ++i + 1; a[i++] = i;
i = i + 1; a[i] = i;
. В списке перечислены различные точки последовательности в приложении C к C11 (и C99 ):
blockquote>
- Ниже приведены точки последовательности, описанные в 5.1.2.3: Между оценки указателя функции и фактических аргументов в вызове функции и фактическом вызове. (6.5.2.2). Между оценками первого и второго операндов следующих операторов: логическое И & amp; & amp; (6.5.13); логический ИЛИ || (6.5.14); запятая, (6.5.17). Между оценками первого операнда условного? : оператор и в зависимости от второго и третьего операндов (6.5.15). Конец полного декларатора: деклараторы (6.7.6); Между оценкой полного выражения и следующим полным выражением, которое должно быть оценено. Ниже приведены полные выражения: инициализатор, не являющийся частью составного литерала (6.7.9); выражение в выражении выражения (6.8.3); управляющее выражение оператора выбора (if или switch) (6.8.4); управляющее выражение while или do (6.8.5); каждое из (необязательных) выражений оператора for (6.8.5.3); (необязательное) выражение в операторе return (6.8.6.4). Непосредственно перед возвратом функции библиотеки (7.1.4). После действий, связанных с каждым форматированным спецификатором преобразования функции ввода / вывода (7.21.6, 7.29.2). Непосредственно перед и сразу после каждого вызова функции сравнения, а также между любым вызовом функции сравнения и любым перемещением объектов, переданных в качестве аргументов для этого вызова (7.22.5).
Формулировка того же абзаца в C11 :
blockquote>
- Если побочный эффект на скалярный объект не влияет на какой-либо другой побочный эффект на один и тот же скалярный объект или вычисление значения с использованием значения одного и того же скалярного объекта, поведение не определено. Если существует несколько допустимых порядков подвыражений выражения, поведение не определено, если такой какой-либо побочный эффект возникает в любом из порядков.84)
Вы можете обнаружить такие ошибки в программе, например, используя последнюю версию GCC с
-Wall
и-Werror
, а затем GCC полностью откажется от компиляции вашей программы. Ниже приведен вывод gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005:% gcc plusplus.c -Wall -Werror -pedantic plusplus.c: In function ‘main’: plusplus.c:6:6: error: operation on ‘i’ may be undefined [-Werror=sequence-point] i = i++ + ++i; ~~^~~~~~~~~~~ plusplus.c:6:6: error: operation on ‘i’ may be undefined [-Werror=sequence-point] plusplus.c:10:6: error: operation on ‘i’ may be undefined [-Werror=sequence-point] i = (i++); ~~^~~~~~~ plusplus.c:14:6: error: operation on ‘u’ may be undefined [-Werror=sequence-point] u = u++ + ++u; ~~^~~~~~~~~~~ plusplus.c:14:6: error: operation on ‘u’ may be undefined [-Werror=sequence-point] plusplus.c:18:6: error: operation on ‘u’ may be undefined [-Werror=sequence-point] u = (u++); ~~^~~~~~~ plusplus.c:22:6: error: operation on ‘v’ may be undefined [-Werror=sequence-point] v = v++ + ++v; ~~^~~~~~~~~~~ plusplus.c:22:6: error: operation on ‘v’ may be undefined [-Werror=sequence-point] cc1: all warnings being treated as errors
Важная часть состоит в том, чтобы знать , что точка последовательности - и что такое точка последовательности, а что нет . Например, оператор запятой является точкой последовательности, поэтому
j = (i ++, ++ i);
четко определен и будет увеличивать
i
на единицу, выдает старое значение, отбрасывает это значение; затем в операторе запятой уложите побочные эффекты; а затем приращениеi
на единицу, и результирующее значение становится значением выражения - т. е. это всего лишь ухищренный способ написатьj = (i += 2)
, который снова является «умным» способом записиi += 2; j = i;
Тем не менее,
,
в списках аргументов функции не - оператор запятой, и нет точки последовательности между оценками различных аргументов; вместо этого они не зависят от друг друга; поэтому вызов функцииint i = 0; printf("%d %d\n", i++, ++i, i);
имеет неопределенное поведение , потому что нет никакой точки последовательности между оценками
i++
и++i
в аргументах функции, а значениеi
поэтому изменяется дважды, какi++
, так и++i
, между предыдущей и следующей точками последовательности.
Вы можете найти здесь setNames
...
rbind(df, setNames(rev(df), names(df)))
# x y
#1 1 3
#2 2 4
#3 3 1
#4 4 2
Я подозреваю, что ваш реальный прецедент несколько сложнее. Вы можете, конечно, изменить порядок столбцов в первом аргументе setNames
по своему усмотрению, просто используйте names(df)
во втором аргументе, чтобы имена переупорядоченных столбцов соответствовали оригиналу.
Это кажется довольно легким:
mapply(c,df,df[,2:1])
x y
[1,] 1 3
[2,] 2 4
[3,] 3 1
[4,] 4 2
Для этого простого случая вам нужно вернуть его обратно в фреймворк данных (поскольку mapply
упрощает его до матрицы):
as.data.frame(mapply(c,df,df[,2:1]))
x y
1 1 3
2 2 4
3 3 1
4 4 2
Важное примечание 1 : Похоже, что обратное отношение типа применяется, когда ваш фреймворк содержит векторы разных типов:
df<-data.frame(x=1:2,y=3:4,z=c('a','b'))
mapply(c,df,df[,c(2:1,3)])
x y z
[1,] 1 3 2
[2,] 2 4 1
[3,] 3 1 2
[4,] 4 2 1
Важное примечание 2 : Это ужасно, если у вас есть факторы.
df<-data.frame(x=factor(1:2),y=factor(3:4))
mapply(c,df[,1:2],df[,2:1])
x y
[1,] 1 1
[2,] 2 2
[3,] 1 1
[4,] 2 2
Итак, если у вас есть все числовые данные, все в порядке.