本文地址:https://www.cnblogs.com/oberon-zjt0806/p/13337577.html

本文参考了这篇文章

三元运算符

(如果您已经了解什么是三元运算符,请大胆第前往下一个章节)

我知道有一元运算符(逻辑非,位反转,负号),二元运算符(加减乘除等),这三元运算符是?

嗯,是的,很多编程语言是支持一种特定的三元运算符(Ternary Operator)的,不过我先不打算用代码的方式来解释这个运算符。我们先以代数的方式来介绍这种运算符。(如果您已经了解什么是三元运算符,请大胆第前往下一个章节)

从代数上来说,我们可以把一个N元运算符(算子)定义为一个N元函数的形式,那么我们假定这个三元运算符叫做\(\Xi_3\),那么实际上,这个三元算子可以被表述为这样一个函数:

\[\Xi_3 \left( o_1,o_2,o_3 \right) =
\begin{cases}
o_2 &,\mathop{\bf{1}} \left( o_1 \right)=1 \\
o_3 &,\mathop{\bf{1}} \left( o_1 \right)=0 \\
\end{cases}
\]

这里面的\(o_1\)到\(o_3\)就是三个运算元,\(\mathop{\bf{1}} (x)\)叫做逻辑幺函数,这个函数采取任意形式的\(o_1\),若\(o_1\)能被解释为\(F\)(逻辑0,逻辑矛盾式)则该函数都输出\(0\),否则总输出\(1\)。

换言之,上面的三元运算\(\Xi_3\)就表示了这样的含义:

若\(o_1\)不能被解释为逻辑0,则\(\Xi_3\)返回\(o_2\),否则\(\Xi_3\)返回\(o_3\)。

通过这样一种运算符可以进行一个很便利的条件选择,很多程序语言中也都提供了这样的运算符,考虑到我们写程序的代码是线性排版的(排在一行里),因此如果不使用函数而是使用运算符构成中缀表达式挤在运算元中间时,我们会发现:

op1 _ op2 _ op3

是的,与二元运算符不同,使用运算符区分三个运算元时需要至少两个字符,放在两个空挡处,因此很多程序语言提供给的是这个运算符?:,也就是:

op1 ? op2 : op3

用起来非常优雅简洁,可以让我们节省大量的代码行数,少些若干肥肥的if语句,尽管大量嵌套的话可读性会下降,不过尽量避免这一点就好。

Lua中的三元运算符

非常遗憾,翻遍整个Lua的参考文档,Lua并没有提供这个东西……

就在听过这个令人沮丧的消息后,我无意中看到了一个这样的解决方法,可以说骚断了我的腰……

(a and {b} or {c})[1]

这种方案使用了一个and和一个or运算符,号称完成了三元运算符的功能,起初我8太相信,但是看到Lua里关于逻辑运算的描述,我终于看懂了……

为什么会这样

Lua可以说是一个步伐六亲不认,不走寻常路的鬼才语言,虽然目前官网上一片死寂。

其中一个不寻常就是,Lua里只有nilfalse可以被解释为逻辑false,其余包括0[[]](空字符串)在内的所有内容全是true

而第二个不寻常的玩法就是,Lua的逻辑运算符andor并不一定返回truefalse,它的返回值满足某种吸收原则,这种吸收原则用一句话表示就是:

若表达式针对当前的逻辑运算符可短路求值,则进行左吸收,否则发生右吸收。

分解到这两个运算符身上就是:

  1. 对于and运算符,表达式a and b会在a解释为false时返回a(左吸收),否则返回b(右吸收)
  2. 对于or运算符,表达式a or b会在a解释为true时返回a(左吸收),否则返回b(右吸收)

这样一来我们回到这个情形:

op1 _1 op2 _2 op3

我们当然是希望op1被解释为true时得到op2,否则得到op3。那我们就具体考虑一下当op1被解释为true时应当怎样,要返回op2,则对于前部op1 _1 op2而言需要发生右吸收,对照上面的吸收规则,那么_1就应当是and。依然考虑op1true,第一次吸收后表达式变成了op2 _2 op3,此时我们希望左吸收,不过这里有个问题,op2被解释为truefalse又是两种情况,我们先考虑op2解释为true的情况,此时要完成左吸收,则个根据吸收规则,_2应当使用or运算符,于是整个表达式变成了op1 and op2 or op3,这个表达式可以解决绝大部分情况。

但是,就如前面所担忧的,这种做法并不能处理op1 and false or op3的情况,因为op1 and false部分会被恒定地置为false,而左falseor运算符无法进行左吸收(因为不能短路求值),这种情况下无论op1是多少都只能返回op3

既然如此,那么我们就需要对op2op3进行包装,Lua第三个不寻常的地方就是那个妖娆的tabletable简直就是个万金油数据结构,什么都能往里塞,而且无论装不装东西,table总能被解释为true,这就不会引发op1 and op2总返回false的情况,从而避免了or的右吸收,于是我们就考虑把op2op3用两个table分别包装起来。

a and {b} or {c}

然而我们希望返回的结果是表里的元素,而非这张表,因此我们取下元素:

(a and {b} or {c})[1] --Lua里下标从1开始

大功告成!

后记

……

……

我还能说什么呢??太马叉虫了!!!

Lua骚操作——三元条件运算符的更多相关文章

  1. 论减少代码中return语句的骚操作

    一.写作背景 最近组内在推行checkstyle代码规范的检测,关于checkstyle的介绍可以参考:https://checkstyle.sourceforge.io, 在按照checkstyle ...

  2. Typescript骚操作,在TS里面直接插入HTML

    Typescript骚操作,在TS里面直接插入HTML,还有语法提示 先给大家看一个图 因为我不喜欢用很重的框架,主要是并非专业UI,但是偶尔会用到,还是觉得直接element组装受不了,想想能在ts ...

  3. 闪电侠 Netty 小册里的骚操作

    前言 即使这是一本小册,但基于"不提笔不读书"的理念,仍然有必要总结一下.此小册对于那些"硬杠 Netty 源码 却不曾在千万级生产环境上使用实操"的用户非常有 ...

  4. awk骚操作

    一.awk自加 [root@168web3 ~]# head /data/logs/cloud_monitor_rds_cpu.log |awk '{sum+=$NF}END{print sum}' ...

  5. 如何在命令长度受限的情况下成功get到webshell(函数参数受限突破、mysql的骚操作)

    0x01 问题提出 还记得上篇文章记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门),我们讲到了一些PHP的一些如何巧妙地绕过数字和字母受限的技巧,今天我要给大家分享的是如 ...

  6. UOJ 117 欧拉回路(套圈法+欧拉回路路径输出+骚操作)

    题目链接:http://uoj.ac/problem/117 题目大意: 解题思路:先判断度数: 若G为有向图,欧拉回路的点的出度等于入度. 若G为无向图,欧拉回路的点的度数位偶数. 然后判断连通性, ...

  7. lua和C++交互的lua栈操作——以LuaTinker为例

    一. -- C++类注册函数(LuaTinker) 的lua栈操作: -- lua栈内容(执行到pop语句) 栈地址 <--执行语句 space_name[name] = t1 -- (2b8) ...

  8. 关于map 及 map 骚操作

    关于map这个东西   很冷门..................   但是,这个博客带你稍微了解一下map:   map用法:一般当作一个下表无穷大的数组   关于它的骚操作:map的鬼畜用法,可以 ...

  9. 通过HTTP的HEADER完成各种骚操作

    作为一名专业的切图工程师,我从来不care网页的header,最多关心Status Code是不是200.但是HEADER真的很重要啊,客户端从服务器端获取内容,首先就是通过HEADER进行各种沟通! ...

随机推荐

  1. Redis 数据结构 之 SDS

    SDS(simple dynamic string),简单动态字符串.s同时它被称为 Hacking String.hack 的地方就在 sds 保存了字符串的长度以及剩余空间.sds 的实现在 sd ...

  2. 基于node的前端项目编译时内存溢出问题

    解决方法: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory JavaScript堆内存不足,这里说的 Jav ...

  3. 万级TPS亿级流水-中台账户系统架构设计

    万级TPS亿级流水-中台账户系统架构设计 标签:高并发 万级TPS 亿级流水 账户系统 背景 业务模型 应用层设计 数据层设计 日切对账 背景 我们需要给所有前台业务提供统一的账户系统,用来支撑所有前 ...

  4. .NET 5 尝鲜 - 开源项目TerminalMACS WPF管理端支持.NET 5

    .NET 5 尝鲜 - 开源项目TerminalMACS WPF管理端支持.NET 5 一个使用 Prism 作为模块化框架.基于多个开源控件库作为UI控件选择.集成开源 UI 界面设计的 .NET ...

  5. 源码剖析@contextlib.contextmanager

    示例 @contextlib.contextmanager def result(a): print('before') yield print('after') 外层装饰源码 包装func函数,真实 ...

  6. Java 从入门到进阶之路(二十六)

    在之前的文章我们介绍了一下 Java 中的  集合框架中的Collection 的子接口 List,本章我们来看一下 Java 集合框架中的Collection 的子接口 Queue. 在之前我们讲 ...

  7. python数据结构(三)

    copy 复制对象,copy模块包含了两个行数copy和deepcopy,用于复制现有的对象. 浅副本(浅复制) copy()创建的浅副本是一个新容器,其中填充了原对象内容的引用 import cop ...

  8. 【Oracle】如何模拟resmgr:cpu quantum

    看完该篇文章你可以了解如下问题:resmgr:cpu quantum等待事件的知识,如何模拟该等待事件,如何避免该事件. 数据库版本: SYS@zkm> select banner from v ...

  9. Vue中$nextTick的理解

    Vue中$nextTick的理解 Vue中$nextTick方法将回调延迟到下次DOM更新循环之后执行,也就是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,能够获取更新后的 ...

  10. Netty 源码解析(六): Channel 的 register 操作

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第六篇. 接下来的时间灯塔君持续更新Netty系列一共九篇   Netty 源码解析(一 ):开始 Netty ...