Expression Language

zen expression 是一个业务优先的表达式语言, 提供优异的性能和简单的可读性, 期待弥合业务分析师和工程师之间的认知偏差。根据表达式的内容和使用的位置, 表达式可以分为两种模式:

  • unary test
  • expression

大多数时候, 你将会在决策表的表格中写 unary test(以逗号分隔的列表).

一元运算符(Unary test)

一元运算符是一个逗号分隔的简单表达式列表,每个逗号分隔符都被视为or运算符, 其计算结果为布尔值。在一元表达式中,有一个特殊符号可 $用于引用当前列。

如果$在列中引用,一元运算符将变成表达式模式

// Given: $ = 1
1, 2, 3      // true
1            // true
>= 1         // true
< 1          // false
[0..10]      // true, (internally this is $ >= 0 and $ <= 10)
> 0 and < 10 // true

// Given: $ = 'USD'
'GBP', 'USD'          // true
'EUR'                 // false
startsWith($, "US")   // true - defaults to expression mode, comma is unavailable
endsWith($, "US")     // false - defaults to expression mode
lower($) == "usd"     // true - defaults to expression mode

表达式(Expression)

表达式具有 ZEN 语言的全部语法功能。它们使您可以访问所有函数,并且在定义列或输出时非常有用。使用时,完整语法在$单元表达式中也可用(因为它强制使用表达式模式)。


100 + 100                              // 200
10 * 5                                 // 50
10 ^ 2                                 // 100
1 in [1, 2, 3]                         // true
5 in (5..10]                           // false
sum([1, 2, 3])                         // 6
max([1, 2, 3])                         // 3
"hello" + " " + "world"                // "hello world"
len("world")                           // 5
weekdayString(date("2022-11-08"))      // "Tue"
contains("hello world", "hello")       // true
upper('john')                          // "JOHN"
some(['admin', 'user'], # == "admin")  // true
not all([100, 200, 400, 800], # in (100..800)) // false
filter([100, 200, 400, 800], # >= 200) // [200, 400, 800]

数据类型

每种数据类型都可以定义自己的一组运算符和内置函数。数据类型定义如下:

字符串(String)

字符串是一种表示文本内容(字符列表、单词)的数据类型。它通过使用单引号'或双引号括住字符来定义"

以下所有示例均有效:

"double quote string";
'single quote string';

字符串操作符

字符串操作符

操作符描述例子
+字符串连接a + b

比较操作符

操作符描述例子
==等于a == b
!=不等于a != b

函数

len

接受一个字符串并返回其中的字符数。

len('string'); // 6

upper

接受一个字符串并返回它的大写版本。

upper('string'); // "STRING"

lower

接受一个字符串并返回其小写版本。

lower('StrInG'); // "string"

startsWith

接受两个参数,如果字符串以指定值开头,则返回 true。

startsWith('Saturday night plans', 'Sat'); // true
startsWith('Saturday night plans', 'Sun'); // false

endsWith

接受两个参数,如果字符串以指定值结尾,则返回 true。

endsWith('Saturday night plans', 'plans'); // true
endsWith('Saturday night plans', 'night'); // false

contains

接受两个参数,如果字符串包含指定值则返回 true。

contains('Saturday night plans', 'night'); // true
contains('Saturday night plans', 'urday'); // true
contains('Saturday night plans', 'Sunday'); // false

matches

接受两个参数,如果字符串与正则表达式匹配则返回 true。

matches('12345', '^d+$'); // true
matches('1234a', '^d+$'); // false

extract

接受两个参数,返回正则表达式中捕获组的数组。

extract('2022-02-01', '(d{4})-(d{2})-(d{2})'); // ["2022-02-01", "2022", "02", "01"]
extract('foo.bar', '(w+).(w+)'); // ["foo.bar", "foo", "bar"]

string

接受一个参数,尝试将变量转换为字符串,失败时抛出错误

string(20); // "20"
string(true); // "true"

数字(Number)

数字可以是整数(整数)或小数(浮点数)。可以使用数字0-9.(小数分隔符)和来定义, 使用_为了便于阅读。

以下所有示例均有效:

100;
1_000_000;
1.54;

在内部,数字使用双精度浮点表示。

数字操作符

一元操作符

操作符描述例子
-取负数-a

算术运算

所有算术运算符都遵循自然数学优先级。例如(a + b) * c不同于a + b * c

操作符描述例子
+加法a + b
-减法a - b
*乘法a * b
/除法a / b
^指数a ^ b
%取余a % b

比较操作符

操作符描述例子
==等于a == b
!=不等于a != b
<小于a < b
>大于a > b
<=小于等于a <= b
>=大于等于a >= b

函数

abs

接受一个数字,并返回其绝对值。

abs(-1.23); // 1.23
abs(10); // 10

rand

接受一个正数(限制),并返回 1 和提供的限制之间的生成数字。

rand(100); // random whole number between 1 and 100, both included
rand(2); // random number, 1 or 2

floor

接受一个数字并将其向下舍入。它返回小于或等于给定数字的最大整数。

floor(5.1); // 5
floor(5.9); // 5

round

接受一个数字并将该数字四舍五入为最接近的整数。

round(5.1); // 5
round(5.9); // 6

ceil

接受一个数字并将其向上舍入。它返回大于或等于给定数字的最小整数。

ceil(5.1); // 6
ceil(5.9); // 6

number

接受一个参数,尝试将数字或字符串转换为数字,失败时抛出错误

number('20'); // 20
number(20); // 20

注意:在决策表中建议number与结合使用isNumeric,这样可以防止表达式失败导致行被跳过。例如isNumeric($) && number($)

isNumeric

接受一个参数,返回 bool,如果变量是数字或可以转换为数字的字符串则返回 true。

isNumeric('20'); // true
isNumeric('test'); // false

布尔(Boolean)

布尔是一种逻辑数据类型,可以是true也可以是false

布尔操作符

逻辑运算符

操作符描述例子
and与(Conjunction)a == b
or或(Disjunction)a != b
!非(Negation)!a
notnot(a)

比较操作符

操作符描述例子
==等于a == b
!=不等于a != b

三元操作符

可以使用三元运算符编写一个简短的内联语句if then else

product.price > 100 ? 'premium' : 'value'; // if price is greater than 100 "premium" otherwise "value"

函数

bool

接受一个参数,尝试将变量转换为布尔值,失败时抛出错误

bool('true'); // true
bool('false'); // false

日期和时间(Date and time)

日期、时间和持续时间是一组虚拟数据类型,内部使用 unix 时间戳(数字)表示为数字。

日期时间操作符

请阅读数字操作符

日期时间函数

date

接受格式化的字符串作为输入并以秒为单位返回 unix 时间戳。

date('now');
date('yesterday');
date('2022-01-01');
date('2022-01-01 16:00');
date('2022-04-04T21:48:30Z');

time

接受格式化的字符串作为输入并返回表示午夜(midnight)秒数的数字。

time('21:49');
time('21:48:20');
time('2022-04-04T21:48:30Z'); // extracts time from date string

duration

接受格式化的字符串(从秒到小时)作为输入并以秒为单位返回持续时间。

duration('1h'); // 3600
duration('30m'); // 1800
duration('10h'); // 36000

dayOfWeek

接受时间戳并以数字形式返回星期几。

dayOfWeek(date('2022-11-08')); // 2

dayOfMonth

接受时间戳并以数字形式返回月份中的日期。

dayOfMonth(date('2022-11-09')); // 9

dayOfYear

接受时间戳并以数字形式返回一年中的某一天。

dayOfYear(date('2022-11-10')); // 314

weekOfMonth

接受时间戳并以数字形式返回月份的周数。

weekOfMonth(date('2022-11-11')); // 2

weekOfYear

接受时间戳并以数字形式返回一年中的第几周。

weekOfYear(date('2022-11-12')); // 45

seasonOfYear

接受 unix 时间戳并以字符串形式返回一年中的季节。

seasonOfYear(date('2022-11-13')); // Autumn

monthString

接受时间戳并以字符串形式返回月份。

monthString(date('2022-11-14')); // Nov

weekdayString

接受时间戳并以字符串形式返回星期几。

weekdayString(date('2022-11-14')); // Mon

startOf

接受时间戳和单位。根据指定单位返回日期的开始时间。 允许的单位:

时间单位传递参数
Second"s" | "second" | "seconds"
Minute"m" | "minute" | "minutes"
Hour"h" | "hour" | "hours"
Day"d" | "day" | "days"
Week"w" | "week" | "weeks"
Month"M" | "month" | "months"
Year"y" | "year" | "years"
startOf(date('2022-11-14 15:45:12'), 'day'); // 2022-11-14 00:00:00

endOf

接受时间戳和单位。根据指定单位返回日期的结束时间。

endOf(date('2022-11-14 15:45:12', 'day')); // 2022-11-14 23:59:59

数组(Array)

数组表示其他数据类型的列表(数字、字符串、布尔值的列表)。它的声明方式首先是左方括号,依次按逗号分隔数据,然后写右方括号表示结束定义。

['a', 'b', 'c'] // string array
[1, 2, 3]       // number array[true, false]   // boolean array

数组运算符

数组成员访问符

数组中的成员可以使用 .key[key] 进行访问。
举个例子:

// Suppose customer.groups = ["admin", "user"]
customer.groups.0  // "admin"
customer.groups[1] // "user

数组元素关系符

运算符描述例子
in元素包含a in array
not in元素不包含a not in array

范围运算符

有用的大于和小于操作符的简化运算符。

操作符等价
闭区间
x in [a..b]x >= a and x <= b
x in (a..b)x > a and x < b
x in [a..b)x >= a and x < b
x in (a..b]x > a and x <= b
开区间
x in ]a..b[x <= a or x >= b
x in )a..b(x < a or x > b
x in ]a..b(x <= a or x > b
x in )a..b[x < a or x >= b

数组函数

len

接受一个数组并返回其长度。

len([1, 2, 3]); // 3

sum

接受一个数字数组并返回所有元素的总和。

sum([1, 2, 3]); // 6

avg

接受一个数字数组并返回所有元素的平均值。

avg([1, 2, 3]); // 2

min

接受一个数字数组并返回最小元素。

min([1, 2, 3]); // 1

max

接受一个数字数组并返回最大元素。

max([1, 2, 3]); // 3

mean

接受一个数字数组并返回平均值。

mean([1, 2, 3]); // 2mean([1, 2, 3, 4]); // 2.5

mode

接受一个数字数组并返回出现最多的元素(众数)。

mode([1, 1, 2, 2, 2, 5, 6, 9]); // 2

contains

接受一个数组和一个搜索参数。如果元素存在于数组中,则返回 true。

contains(['a', 'b', 'c'], 'a'); // true
contains([1, 2, 3], 5); // false

flatten

接受数组并通过单个级别展平参数。

flatten([1, 'a', ['b', 'c'], [4]]); // [1, "a", "b", "c", 4]
flatten([  [1, 2, 3],  [4, 5, 6],]); // [1, 2, 3, 4, 5, 6]

闭包函数

闭包函数允许您将回调定义为特殊参数,该参数会迭代数组的所有元素。在闭包中,您可以使用变量访问当前元素#

all

如果数组的所有元素都满足条件,则返回 true。

all(["a", "b"], # == "a")   // false
all([1, 2, 3], # in [1..3]) // true

some

如果数组中至少有一个元素满足条件,则返回 true。

some(["a", "b"], # == "a") // true
some([1, 2, 3], # > 5)     // false

none

如果数组中没有元素满足条件,则返回 true。

none(["a", "b"], # == "a") // false
none([1, 2, 3], # > 5)     // true

filter

返回一个仅包含满足条件的元素的新数组。

filter([1, 2, 3, 4], # > 1) // [2, 3, 4]

map

返回具有重新映射值的新数组。

map(["world", "user"], "hello " + #) // ["hello world", "hello user"]
map([1, 2, 3], # + 5)                // [6, 7, 8]

flatMap

返回具有重新映射值的新数组并将其展平。

flatMap([[1, 2, 3], [4, 5, 6]], map(#, # + 1)) // [2, 3, 4, 5, 6, 7]

count

对数组中的元素按照过滤条件计数。

count([1, 2, 3], # > 1) // 2

上下文(Context)

Context 是一种特殊的全局数据类型,其结构与节点接收到的 JSON 输入相同。

它使用成员资格运算.符来访问其成员。

例子

如果输入收到以下内容:

{  "customer": {    
       "firstName": "John",    
       "lastName": "Doe",    
       "groups": ["admin", "user"],    
       "age": 34  
    }
}

我们将能够全局使用以下属性:

customer.firstName; // "John"
customer.lastName; // "Doe"
customer.groups; // ["admin", "user"]
customer.age; // 34

为了将其与前面几节的知识相结合,我们可以写出如下表达式:

customer.firstName + " " + customer.lastName // "John Doe"
customer.age in [30..40] // true
contains(customer.groups, "admin") // true

$也被认为是上下文的一部分,它指的是当前列(单元格内)。