jsonata工具查询JSON和转换

jsonata工具查询JSON和转换

参考网址:http://docs.jsonata.org/1.7.0/date-time
https://github.com/IBM/JSONata4Java

简介

JSONata是IBM的OPen Source项目,正是这样做的。它是一种用于查询和转换 JSON 数据结构的表达式语言。JSONata 是一种用于查询和转换 JSON 数据的强大表达式语言。在 Java 中,可以使用 com.api.jsonata4java 这个库来整合 JSONata 的功能。这个库实现了 JSONata 表达式的解析和执行,使得在 Java 应用程序中能够方便地对 JSON 数据进行复杂的查询和转换操作。

pom依赖引入:

<dependency>
    <groupId>com.ibm.jsonata4java</groupId>
    <artifactId>JSONata4Java</artifactId>
    <version>1.4.6</version>
</dependency>

JSON 对象

JSON 对象是一个关联数组(也称为映射或哈希)。导航到任意深度嵌套结构的 JSON 对象的位置路径语法由点 ‘.’ 分隔符分隔的字段名称组成。该表达式返回导航到位置路径中的最后一步后引用的 JSON 值。如果在位置路径的导航过程中找不到字段,则表达式不返回任何内容(由 Javascript undefined 表示)。不会因输入文档中不存在的数据而引发任何错误。

除非另有说明,否则本指南中的示例将使用以下示例 JSON 文档:

{
  "FirstName": "Fred",
  "Surname": "Smith",
  "Age": 28,
  "Address": {
    "Street": "Hursley Park",
    "City": "Winchester",
    "Postcode": "SO21 2JN"
  },
  "Phone": [
    {
      "type": "home",
      "number": "0203 544 1234"
    },
    {
      "type": "office",
      "number": "01962 001234"
    },
    {
      "type": "office",
      "number": "01962 001235"
    },
    {
      "type": "mobile",
      "number": "077 7700 1234"
    }
  ],
  "Email": [
    {
      "type": "work",
      "address": ["fred.smith@my-work.com", "fsmith@my-work.com"]
    },
    {
      "type": "home",
      "address": ["freddy@my-social.com", "frederic.smith@very-serious.com"]
    }
  ],
  "Other": {
    "Over 18 ?": true,
    "Misc": null,
    "Alternative.Address": {
      "Street": "Brick Lane",
      "City": "London",
      "Postcode": "E1 6RF"
    }
  }
}

以下表达式在应用于此 JSON 文档时将生成以下结果:

以下表达式在应用于此 JSON 文档时将生成以下结果:

表达输出评论
Surname"Smith"返回 JSON 字符串(“双引号”)
Age28返回一个 JSON 数字
Address.City"Winchester"字段引用,以 "."分隔
Other.Miscnull匹配路径并返回空值
Other.Nothing找不到路径。返回未定义的 Javascript
Other.'Over 18 ?'true包含空格或保留标记的 字段引用可以括在引号中(单引号或双引号)
    public static ObjectMapper objectMapper = new ObjectMapper();

    public final String jsonata="{\n" +
        "  \"FirstName\": \"Fred\",\n" +
        "  \"Surname\": \"Smith\",\n" +
        "  \"Age\": 28,\n" +
        "  \"Address\": {\n" +
        "    \"Street\": \"Hursley Park\",\n" +
        "    \"City\": \"Winchester\",\n" +
        "    \"Postcode\": \"SO21 2JN\"\n" +
        "  },\n" +
        "  \"Phone\": [\n" +
        "    {\n" +
        "      \"type\": \"home\",\n" +
        "      \"number\": \"0203 544 1234\"\n" +
        "    },\n" +
        "    {\n" +
        "      \"type\": \"office\",\n" +
        "      \"number\": \"01962 001234\"\n" +
        "    },\n" +
        "    {\n" +
        "      \"type\": \"office\",\n" +
        "      \"number\": \"01962 001235\"\n" +
        "    },\n" +
        "    {\n" +
        "      \"type\": \"mobile\",\n" +
        "      \"number\": \"077 7700 1234\"\n" +
        "    }\n" +
        "  ],\n" +
        "  \"Email\": [\n" +
        "    {\n" +
        "      \"type\": \"work\",\n" +
        "      \"address\": [\"fred.smith@my-work.com\", \"fsmith@my-work.com\"]\n" +
        "    },\n" +
        "    {\n" +
        "      \"type\": \"home\",\n" +
        "      \"address\": [\"freddy@my-social.com\", \"frederic.smith@very-serious.com\"]\n" +
        "    }\n" +
        "  ],\n" +
        "  \"Other\": {\n" +
        "    \"Over 18 ?\": true,\n" +
        "    \"Misc\": null,\n" +
        "    \"Alternative.Address\": {\n" +
        "      \"Street\": \"Brick Lane\",\n" +
        "      \"City\": \"London\",\n" +
        "      \"Postcode\": \"E1 6RF\"\n" +
        "    }\n" +
        "  }\n" +
        "}";

    public final JSONObject jsonata2=JSONObject.parseObject(jsonata);
    @org.junit.Test
    public void testSwitch() {
        try {
            Expression firstName = Expression.jsonata("FirstName");
            JsonNode evaluate = firstName.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(evaluate);
            Expression otherExression = Expression.jsonata("Other.'Over 18 ?'");
            JsonNode evaluate1 = otherExression.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(evaluate1);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (EvaluateException e) {
            throw new RuntimeException(e);
        }
    }

JSON 数组

当需要有序的值集合时,将使用 JSON 数组。数组中的每个值都与索引(位置)而不是名称相关联,因此为了处理数组中的各个值,需要额外的语法来指定索引。这是在数组的字段名称后使用方括号完成的。如果方括号包含数字或计算结果为数字的表达式,则该数字表示要选择的值的索引。索引是零偏移量,即数组中的第一个值是 。如果数字不是整数,则将其向下舍入为整数。如果方括号中的表达式是非数字的,或者是计算结果不为数字的表达式,则将其视为谓词。arr``arr[0]

例如,从数组末尾开始计数的负索引将选择最后一个值、倒数第二个值等。如果指定的索引超过数组的大小,则不会选择任何内容。arr[-1]``arr[-2]

如果没有为数组指定索引(即字段引用后没有方括号),则选择整个数组。如果数组包含对象,并且位置路径选择这些对象中的字段,则数组中的每个对象都将被查询以进行选择。

表达输出评论
Phone[0]{ "type": "home", "number": "0203 544 1234" }返回第一项(一个对象)
Phone[1]{ "type": "office", "number": "01962 001234" }返回第二项
Phone[-1]{ "type": "mobile", "number": "077 7700 1234" }返回最后一项
Phone[-2]{ "type": "office", "number": "01962 001235" }从末尾开始的负索引计数
Phone[8]不存在 - 不返回任何内容
Phone[0].number"0203 544 1234"选择第一项中的字段number
Phone.number[ "0203 544 1234", "01962 001234", "01962 001235", "077 7700 1234" ]没有给出索引,因此它选择所有 字段(整个数组),然后为每个字段选择所有字段Phone``number
Phone.number[0][ "0203 544 1234", "01962 001234", "01962 001235", "077 7700 1234" ]可能期望它只返回第一个数字, 但它返回每个项目的第一个数字Phone
(Phone.number)[0]"0203 544 1234"将索引应用于 返回的数组。括号的一次使用。Phone.number
顶级数组、嵌套数组和数组扁平化

考虑 JSON 文档:

[
  { "ref": [ 1,2 ] },
  { "ref": [ 3,4 ] }
]

在顶层,我们有一个数组而不是一个对象。如果我们要选择此顶级数组中的第一个对象,则没有要追加到的字段名称。我们不能单独使用,因为这与数组构造函数语法冲突。但是,我们可以使用上下文引用来引用文档的开头,如下所示:[0]``[0]``$

表达输出评论
$[0]{ "ref": [ 1,2 ] }$表达式开头引用整个输入文档
$[0].ref[ 1,2 ].ref这里返回整个内部数组
$[0].ref[0]1返回内部数组第一个位置上的元素
$.ref[ 1, 2, 3, 4 ]尽管嵌套数组的结构不同,但生成的选择 被平展为单个平面数组。输入数组的原始嵌套结构 将丢失。有关如何在结果中 维护原始结构的信息,请参阅数组构造函数。
    @org.junit.Test
    public void testArray() {

        try {
            //json数组
            Expression phoneEx1 = Expression.jsonata("Phone[0]");
            JsonNode phone1 = phoneEx1.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(phone1);
            Expression phoneEx2 = Expression.jsonata("Phone[-1]");
            JsonNode phone2 = phoneEx2.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(phone2);
            Expression phoneEx3 = Expression.jsonata("Phone[0].number");
            JsonNode phone3 = phoneEx3.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(phone3);
            Expression numbersEx3 = Expression.jsonata("Phone.number");
            JsonNode numbers = numbersEx3.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(numbers);
            //顶级数组
            System.out.println("================================================================================");
            JSONArray jsonArray = new JSONArray();
            jsonArray.add(jsonata2);
            jsonArray.add(jsonata2);
            Expression firstJSonObjectEx = Expression.jsonata("$[0]");
            JsonNode firstJSonObject = firstJSonObjectEx.evaluate(objectMapper.valueToTree(jsonArray));
            System.out.println(firstJSonObject);
            Expression allPhoneNumbersEx = Expression.jsonata("$.Phone.number");
            JsonNode allPhoneNumbers = allPhoneNumbersEx.evaluate(objectMapper.valueToTree(jsonArray));
            System.out.println(allPhoneNumbers);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (EvaluateException e) {
            throw new RuntimeException(e);
        }

    }

复杂选择

通配符

使用字段名称代替字段来选择对象中的所有字段*

表达输出评论
Address.*[ "Hursley Park", "Winchester", "SO21 2JN" ]选择Address
*.Postcode"SO21 2JN"选择任何子对象的值Postcode
    @org.junit.Test
    public void test2() {
        try {
            Expression addressValuesEx = Expression.jsonata("Address.*");
            JsonNode addRessValues = addressValuesEx.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(addRessValues);
            System.out.println("===========================================================");
            Expression postCodeEx = Expression.jsonata("*.Postcode");
            JsonNode postCode = postCodeEx.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(postCode);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (EvaluateException e) {
            throw new RuntimeException(e);
        }
    }
导航任意深度

后代通配符将遍历所有后代(多级通配符)。**``*

表达输出评论
**.Postcode[ "SO21 2JN", "E1 6RF" ]选择所有值,无论它们在结构中的嵌套深度如何Postcode
Expression postCodeEx = Expression.jsonata("*.Postcode");
JsonNode postCode = postCodeEx.evaluate(objectMapper.valueToTree(jsonata2));
System.out.println(postCode);
谓词

在位置路径的任何步骤中,都可以使用谓词 - [expr] 筛选所选项目,其中 expr 的计算结果为布尔值。选择中的每个项目都针对表达式进行测试,如果它的计算结果为 true,则保留该项目;如果为 false,则将其从所选内容中删除。表达式是相对于正在测试的当前(上下文)项计算的,因此,如果谓词表达式执行导航,则它是相对于此上下文项计算的。

例子:

表达输出评论
Phone[type='mobile']{ "type": "mobile", "number": "077 7700 1234" }选择字段等于 的项目。Phone``type``"mobile"
Phone[type='mobile'].number"077 7700 1234"选择手机号码
Phone[type='office'].number[ "01962 001234", "01962 001235" ]选择办公室电话号码 - 其中有两个!
    @org.junit.Test
    public void test3() {
        try {
            Expression phoneTypeEx = Expression.jsonata("Phone[type='mobile']");
            JsonNode phoneType = phoneTypeEx.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(phoneType);
            Expression phoneTypeEx1 = Expression.jsonata("Phone[type='mobile'].number");
            JsonNode phoneType1 = phoneTypeEx1.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(phoneType1);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (EvaluateException e) {
            throw new RuntimeException(e);
        }
    }
单例数组和值等效性

在 JSONata 表达式或子表达式中,任何值(其本身不是数组)和仅包含该值的数组都被视为等效的。这允许语言是可组合的,以便从中提取单个值的位置路径以及从数组中提取多个值的对象和位置路径都可以用作其他表达式的输入,而无需为这两种形式使用不同的语法。

请考虑以下示例:

  • Address.City返回单个值"Winchester"
  • Phone[0].number匹配单个值,并返回该值"0203 544 1234"
  • Phone[type='home'].number同样匹配单个值"0203 544 1234"
  • Phone[type='office'].number匹配两个值,因此返回一个数组[ "01962 001234", "01962 001235" ]

在处理 JSONata 表达式的返回值时,无论匹配了多少个值,都可能需要以一致的格式显示结果。在上面的前两个表达式中,很明显,每个表达式都在处理结构中的单个值,并且只返回该值是有意义的。但是,在最后两个表达式中,匹配的值并不明显,如果宿主语言必须根据返回的内容以不同的方式处理结果,则没有帮助。

如果这是一个问题,则可以修改表达式以使其返回数组,即使只匹配单个值。这是通过向位置路径中的步骤添加空方括号来完成的。可以重写上面的示例以始终返回数组,如下所示:[]

  • Address[].City返回[ "Winchester"]
  • Phone[0][].number返回[ "0203 544 1234" ]
  • Phone[][type='home'].number返回[ "0203 544 1234" ]
  • Phone[type='office'].number[]返回[ "01962 001234", "01962 001235" ]

请注意,可以放置在谓词的任一侧以及路径表达式中的任何步骤上[]

    @org.junit.Test
    public void test4() {
        try {
            Expression addCityEx = Expression.jsonata("Address.City[]");
            JsonNode addCity = addCityEx.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(addCity);
            Expression phoneTypeEx1 = Expression.jsonata("Phone[type='office'].number[]");
            JsonNode phoneType = phoneTypeEx1.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(phoneType);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (EvaluateException e) {
            throw new RuntimeException(e);
        }
    }

组合值

字符串表达式

指向字符串值的路径表达式将返回该值。可以使用串联运算符"&"组合字符串

例子

表达输出评论
FirstName & ' ' & Surname"Fred Smith"连接后跟空格 ,后跟FirstName``Surname
Address.(Street & ', ' & City)"Hursley Park, Winchester"括号的另一个很好的用法
    @org.junit.Test
    public void test5() {
        try {
            Expression firstNameAndSurNameEx = Expression.jsonata("FirstName & ' ' & Surname");
            JsonNode firstNameAndSurName =                                     firstNameAndSurNameEx.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(firstNameAndSurName);
            Expression addressStreetAndCityEx = Expression.jsonata("Address.(Street & ', ' & City)");
            JsonNode addressStreetAndCity = addressStreetAndCityEx.evaluate(objectMapper.valueToTree(jsonata2));
            System.out.println(addressStreetAndCity);

        } catch (ParseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (EvaluateException e) {
            throw new RuntimeException(e);
        }

    }

请考虑以下 JSON 文档:

{
  "Numbers": [1, 2.4, 3.5, 10, 20.9, 30]
}
数值表达式

指向数字值的路径表达式将返回该值。可以使用通常的数学运算符将数字组合在一起以生成结果数字。支持的运算符:

  • +加法
  • -减法
  • *乘法
  • /划分
  • %余数(模)

例子

表达输出评论
Numbers[0] + Numbers[1]3.4添加 2 个价格
Numbers[0] - Numbers[4]-19.9减法
Numbers[0] * Numbers[5]30将价格乘以数量
Numbers[0] / Numbers[4]0.04784688995215划分
Numbers[2] % Numbers[5]3.5模运算符
比较表达式

通常用于谓词中,用于比较两个值。返回布尔值 true 或 false 支持的运算符:

  • =等于
  • !=不等于
  • <小于
  • <=小于或等于
  • >大于
  • >=大于或等于
  • in值包含在数组中

例子

表达输出评论
Numbers[0] = Numbers[5]平等
Numbers[0] != Numbers[4]不等式
Numbers[1] < Numbers[5]小于
Numbers[1] <= Numbers[5]小于或等于
Numbers[2] > Numbers[4]大于
Numbers[2] >= Numbers[4]大于或等于
"01962 001234" in Phone.number值包含在
布尔表达式

用于组合布尔结果,通常用于支持更复杂的谓词表达式。支持的运算符:

  • and
  • or

请注意,它作为函数而不是运算符受支持。not

例子

表达输出评论
(Numbers[2] != 0) and (Numbers[5] != Numbers[1])and算子
(Numbers[2] != 0) or (Numbers[5] = Numbers[1])or算子

指定结果结构

到目前为止,我们已经发现了如何从JSON文档中提取值,以及如何使用数字,字符串和其他运算符操作数据。能够指定此处理后的数据在输出中的显示方式非常有用。

数组构造函数

如前所述,当位置路径与输入文档中的多个值匹配时,这些值将作为数组返回。这些值可能是对象或数组,因此将具有自己的结构,但匹配的值本身位于生成的数组中的顶层。

通过指定位置路径表达式中数组(或对象)的构造,可以在生成的数组中构建额外的结构。在需要字段引用的位置路径中的任何点,都可以插入一对方括号,以指定这些括号内的表达式结果应包含在输出的新数组中。逗号用于分隔数组构造函数中的多个表达式。[]

数组构造函数也可以在位置路径中使用,以便在没有广泛使用通配符的情况下进行多次选择。

例子:

表达输出评论
Email.address[ "fred.smith@my-work.com", "fsmith@my-work.com", "freddy@my-social.com", "frederic.smith@very-serious.com" ]四个电子邮件地址以平面数组形式返回
Email.[address][ [ "fred.smith@my-work.com", "fsmith@my-work.com" ], [ "freddy@my-social.com", "frederic.smith@very-serious.com" ] ]每个电子邮件对象都会生成一个地址数组
[Address, Other.'Alternative.Address'].City[ "Winchester", "London" ]会提取Address和Other.'Alternative.Address’中能匹配到城市的数组
对象构造函数

与构造数组的方式类似,也可以在输出中构造 JSON 对象。在位置路径中需要字段引用的任何点,一对大括号包含以逗号分隔的键/值对,每个键和值以冒号分隔:。键和值可以是文字,也可以是表达式。键必须是字符串或计算结果为字符串的表达式。{}``{key1: value2, key2:value2}
当对象构造函数遵循选择多个值的表达式时,对象构造函数将创建一个对象,其中包含每个上下文值的键/值对。如果需要一组对象(每个上下文值一个),则对象构造函数应立即跟在点“.”之后。操作员。

例子:

表达式输出评论
Phone{type: number}[{“number”:“0203 544 1234”,“type”:“home”},{“number”:“01962 001234”,“type”:“office”},{“number”:“01962 001235”,“type”:“office”},{“number”:“077 7700 1234”,“type”:“mobile”}]会生成一个对象,type作为key,number也作为key,区提取相应的值,如果有重复的会形成覆盖
Phone.{type: number}[{“number”:“0203 544 1234”,“type”:“home”},{“number”:“01962 001234”,“type”:“office”},{“number”:“01962 001235”,“type”:“office”},{“number”:“077 7700 1234”,“type”:“mobile”}]
JSON迭代

数组和对象构造函数使用 JSON 数组和 JSON 对象的标准 JSON 语法。除此之外,其他 JSON 数据类型的值可以使用其本机 JSON 语法输入到表达式中:

  • strings - "hello world"
  • numbers - 34.5
  • Booleans - or true``false
  • nulls - null
  • objects - {"key1": "value1", "key2": "value2"}
  • arrays - ["value1", "value2"]

这意味着任何有效的 JSON 文档也是有效的表达式。此属性允许您使用 JSON 文档作为所需输出的模板,然后用表达式替换其中的一部分,以将数据插入到输入文档的输出中。

编程构造

到目前为止,我们已经介绍了该语言的所有部分,这些部分允许我们从输入 JSON 文档中提取数据,使用字符串和数字运算符组合数据,以及设置输出 JSON 文档的结构格式。以下是将其转换为图灵完备的函数式编程语言的部分。

条件表达式

If/then/else 构造可以使用三元运算符 "? :"编写。predicate ? expr1 : expr2

将计算表达式。如果其有效布尔值(参见定义)然后被计算并返回,否则被计算并返回。predicate``true``expr1``expr2

带括号的表达式和块

用于覆盖运算符优先级规则。例如

  • (5 + 3) * 4

用于计算上下文值上的复杂表达式

  • Product.(Price * Quantity)- "价格"和"数量"都是"产品"值的字段

用于支持"代码块" - 多个表达式,用分号分隔

(expr1; expr2; expr3)

块中的每个表达式都按顺序计算;最后一个表达式的结果从块返回。

变量

任何以美元"$"开头的名称都是一个变量。变量是对值的命名引用。该值可以是语言类型系统(链接)中的任何类型的值之一。

内置变量
  • $没有名称的变量引用输入 JSON 层次结构中任何点的上下文值。例子
  • $$输入 JSON 的根。仅当您需要脱离当前上下文以暂时沿着不同的路径导航时才需要。例如,用于交叉引用或联接数据。例子
  • 本机(内置)函数。请参阅函数库。
变量赋值

值(类型系统中的任何类型的值)可以分配给变量

$var_name := "value"

存储的值以后可以使用表达式 引用。$var_name

变量的作用域仅限于分配它的"块"。例如

Invoice.(
  $p := Product.Price;
  $q := Product.Quantity;
  $p * $q
)

返回发票中产品的价格乘以数量。

功能

该函数是一等类型,可以像任何其他数据类型一样存储在变量中。提供内置函数库(链接),并将其分配给全局作用域中的变量。例如,包含一个函数,当使用字符串参数调用时,该函数将返回一个字符串,其中所有字符都更改为大写。$uppercase``str``str

调用函数

函数的调用方式是在其引用(或定义)之后加上包含逗号分隔的参数序列的括号。例子:

  • $uppercase("Hello")返回字符串"HELLO"。
  • $substring("hello world", 0, 5)返回字符串"hello"
  • $sum([1,2,3])返回数字 6
定义函数

可以使用以下语法定义匿名 (lambda) 函数:

function($l, $w, $h){ $l * $w * $h }

并且可以使用以下命令调用

function($l, $w, $h){ $l * $w * $h }(10, 10, 5)返回 500

该函数也可以分配给变量以供将来使用(在块内)

(
  $volume := function($l, $w, $h){ $l * $w * $h };
  $volume(10, 10, 5);
)
递归函数

已分配给变量的函数可以使用该变量引用调用自身。这允许定义递归函数。例如。

(
  $factorial:= function($x){ $x <= 1 ? 1 : $x * $factorial($x-1) };
  $factorial(4)
)

请注意,实际上可以使用纯匿名函数编写递归函数(即,不会将任何内容分配给变量)。这是使用Y组合器完成的,对于那些对函数式编程感兴趣的人来说,这可能是一个有趣的转移。

高阶函数

函数作为一等数据类型,可以作为参数传递给另一个函数,也可以从函数返回。处理其他函数的函数称为高阶函数。请考虑以下示例:

(
  $twice := function($f) { function($x){ $f($f($x)) } };
  $add3 := function($y){ $y + 3 };
  $add6 := $twice($add3);
  $add6(7)
)
  • 存储在变量中的函数是高阶函数。它采用一个作为函数的参数,并返回一个函数,该函数采用一个参数,该参数在调用时将该函数应用于 两次。$twice``$f``$x``$f``$x
  • $add3存储一个向其参数添加 3 的函数。两者都不是或尚未被调用。$twice``$add3
  • $twice通过将函数作为其参数传递来调用。这将返回一个函数,该函数对其参数应用两次。此返回的函数尚未调用,而是分配给变量 。add3``$add3``add6
  • 最后,使用参数 7 调用 in 中的函数,导致 3 被添加到其中两次。它返回 13。$add6
函数是闭包

定义 lambda 函数时,评估引擎会获取环境的快照,并将其与函数体定义一起存储。环境包括上下文项(即位置路径中的当前值)以及当前范围内的变量绑定。稍后调用 lambda 函数时,将在调用时在该存储环境中而不是在当前环境中执行此操作。此属性称为词法范围,闭包的基本属性。

请考虑以下示例:

Account.(
  $AccName := function() { $.'Account Name' };

  Order[OrderID = 'order104'].Product.{
    'Account': $AccName(),
    'SKU-' & $string(ProductID): $.'Product Name'
  }
)

创建函数时,上下文项(由 "$"表示)的值为 。稍后,当调用该函数时,上下文项已将结构向下移动到每个项的值。但是,函数体是在定义时存储的环境中调用的,因此其上下文项的值为 。这是一个有点人为的例子,你真的不需要一个函数来做到这一点。该表达式生成以下结果:Account``Product``Account

{
  "Account": "Firefly",
  "SKU-858383": "Bowler Hat",
  "SKU-345664": "Cloak"
}
高级的东西

无需阅读本节 - 它不会对您的理智或操作JSON数据的能力有任何帮助。

早些时候,我们学会了如何编写递归函数来计算数字的阶乘,并暗示可以在不命名任何函数的情况下完成此操作。我们可以将高阶函数发挥到极致,并编写以下内容:

λ($f) { λ($x) { $x($x) }( λ($g) { $f( (λ($a) {$g($g)($a)}))})}(λ($f) { λ($n) { $n < 2 ? 1 : $n * $f($n - 1) } })(6)

这将产生结果。可以使用希腊语lambda(λ)符号代替单词,如果您可以在键盘上找到它,将节省屏幕空间并取悦lambda演算的粉丝。720``function

上述表达式的第一部分是用这种语言实现的 Y 组合器。我们可以将其赋值给一个变量,并将其应用于其他递归匿名函数:

(
  $Y := λ($f) { λ($x) { $x($x) }( λ($g) { $f( (λ($a) {$g($g)($a)}))})};
  [1,2,3,4,5,6,7,8,9] . $Y(λ($f) { λ($n) { $n <= 1 ? $n : $f($n-1) + $f($n-2) } }) ($)
)

产生斐波那契级数。[ 1, 1, 2, 3, 5, 8, 13, 21, 34 ]

但我们不需要做任何这些。使用命名函数更明智:

(
  $fib := λ($n) { $n <= 1 ? $n : $fib($n-1) + $fib($n-2) };
  [1,2,3,4,5,6,7,8,9] . $fib($)
)
  • 26
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值