Тернарная операция

Ветераны, знакомые с C/C++, знают тернарную операцию a ? b : c. Использование этого синтаксиса для замены простых условных операторов позволяет сохранить код максимально кратким, не делая его при этом труднее для чтения.

int a, b, ret;
//if-else
if (a > b)
    ret = a;
else
    ret = b;

//三目运算符
ret = a > b ? a : b;

Тернарная операция в Lua

В нативной семантике Lua тернарная операция не реализована; обычно её имитируют с помощью логических операторов and и or.

В Lua и and, и or используют «сокращённое вычисление» (short-circuit evaluation), то есть вычисляют второй операнд только при необходимости. («Programming in Lua»)

local a, b, ret;
ret = a > b and a or b

Перечислим все возможности:

Когда a > b:

a > b and a –> true 
a or b –> a

Когда a <= b:

a > b and a –> false 
a or b –> b

Отлично!

Подводные камни тернарного оператора в Lua

Следуя принципу перехода от частного к общему:

  • Общая форма тернарной операции a ? b : c

    a = true, результат — b
    a = false, результат — c

  • Соответствует a and b or c в Lua

    • b = true
      • a = true
        • a and b –> true
        • b or c –> b
      • a = false
        • a and b –> false
        • b or c –> c
    • b = false
      • a = true
        • a and b –> false
        • b or c –> c
      • a = false
        • a and b –> false
        • b or c –> c

Видно, что когда b = false, эмулируемое в Lua выражение a and b or c всегда возвращает c и не может восстановить исходное поведение тернарного оператора.

«Программирование на Lua» также рекомендует использовать if-else, чтобы избежать этой ситуации.

Обобщённый вариант a and b or c

Есть ли способ решить проблему, когда b = false не срабатывает?

Это напоминает мне распространённый экзаменационный вопрос по языку C: напишите макроопределение, возвращающее меньшее из двух значений.

Когда я впервые увидел эту задачу в школе, моей первой реакцией было:

#define MIN(A,B) A < B ? A : B

Однако такой способ записи не может возвращать правильные результаты во многих контекстах вложенного использования. Например: 2 * MIN(3, 4) раскрывается в 2 * 3 < 4 ? 3 : 4 = 6 < 4 ? 3 : 4, и результат равен 4.
Более «инженерный» стиль написания:

#define MIN(A,B) ((A) < (B) ? (A) : (B))

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

Из этого примера я начал задумываться: как убедиться, что b в a and b or c истинно, или что b не создаёт неоднозначности?

  • У and приоритет оператора выше, чем у or; простое изменение порядка операций не помогает.
  • Затем я вспомнил об универсальной таблице в Lua. Можно ли поместить все a, b и c в таблицы, чтобы изменить присутствие b? Заметьте, что {nil} также является объектом, который считается истинным.
    Замените все a, b и c на таблицы: {a} and {b} or {c}.
  • В тернарной операции a — это условие, а результат — b или c. На самом деле a не нужно помещать в таблицу; иначе {a} всегда было бы истинным, что лишает смысла условие. А {b} или
    результат {c} обязательно является таблицей, и эта таблица содержит только один элемент. Тогда к ней можно получить доступ через
  • Итак, более обобщённая тернарная операция в Lua выглядит так: (a and {b} or {c})

Итог

Признаюсь, чтобы узнать форму тернарной операции в Lua, достаточно просто поискать её и найти нужный ответ. Приведённый выше ход рассуждений несколько подозрителен: кажется, что ответ уже известен и к нему намеренно подводят. Однако я твёрдо убеждён, что при доказательстве задачи вывод — не самое главное; главное — процесс аргументации. В этой статье предпринята попытка восстановить вывод обобщённой тернарной операции в Lua, основываясь только на моём поверхностном понимании Lua. Если есть лучший ход рассуждений, мы можем обменяться мнениями.

Источник: https://blog.csdn.net/u010832643/article/details/77546887