连接检查

连接检查会限制哪些连接(以及相应的块)可以相互连接。

连接检查对于建模类型很有用。例如,以下三个代码块没有关联的业务,因为它们代表返回不同类型的代码:

一个空列表块,连接到平方根块,再连接到大写块

连接检查可用于防止这些块进行连接。这样可以为用户提供即时反馈,并防止许多简单的错误。

运作方式

每个连接都可以与“连接检查”相关联,连接检查是可为 null 的字符串数组。

如果出现以下情况,则两个连接可以连接:

  1. 它们是兼容的类型(例如,连接到输入的输出)。
  2. 它们的连接检查中至少有一个字符串。

例如,以下两个检查可能会连接,因为它们共用 'apple' 字符串:

['apple', 'ball', 'cat']
['apple', 'bear', 'caterpillar']

但这两项检查无法连接,因为它们不共享任何字符串:

['apple', 'ball', 'cat']
['ape', 'bear', 'caterpillar']

还有一种特殊情况。如果任一数组为 null,则两个连接也可以连接。这样,您就可以定义可连接到任何内容的连接。

null
['ape', 'bear', 'caterpillar]

设置检查

默认情况下,所有连接都会进行 null 连接检查,这意味着它们可以连接到任何内容。您需要手动分配连接检查。

为连接分配连接检查的方式取决于您使用的是 JSON 块定义还是 JavaScript 块定义。

JSON

对于顶级连接,您可以将检查直接分配给定义连接的属性。您分配的值可以是 null、字符串(将成为连接检查中的唯一条目)或字符串数组。

{
  'type': 'custom_block',

  'output': null,
  'nextStatement': 'a connection check entry',
  'previousStatement': ['four', 'connection', 'check', 'entries']
}

对于输入,您可以将检查分配给输入定义的 check 属性。如果 check 属性不存在,则检查会被视为 null。您分配的值可以是字符串或字符串数组。

{
  'type': 'custom_block',
  'message0': '%1 %2',

  'args0': [
    {
      'type': 'input_value',
      'check': 'a connection check entry'
    },
    {
      'type': 'input_statement',
      'check': ['four', 'connection', 'check', 'entries']
    }
  ]
}

JavaScript

对于顶级连接,您可以将检查直接传递给定义连接的方法。如果您未传递值,则检查会被视为 null。您传递的值可以是字符串(将成为连接检查中的唯一条目),也可以是字符串数组。

Blockly.Blocks['custom_block'] = {
  init: function() {
    this.setOutput(true); // null check
    this.setNextStatement(true, 'a connection check entry');
    this.setPreviousStatement(true, ['four', 'connection', 'check', 'entries']);
  }
}

对于输入,您可以在定义输入后将检查传递给 setCheck 方法。如果未调用 setCheck 方法,则检查会被视为 null。您传递的值可以是字符串或字符串数组。

Blockly.Blocks['custom_block'] = {
  init: function() {
    this.appendValueInput('NAME')
        .setCheck('a connection check entry');
    this.appendStatementInput('NAME')
        .setCheck(['four', 'connection', 'check', 'entries']);
  }
}

内置检查字符串

内置代码块具有值为 'Array''Boolean''Colour''Number''String' 的连接检查。如果您希望块与内置块进行互操作,可以使用这些值使其兼容。

值示例

在为输入和输出定义连接检查时,通常应将检查视为表示类型。

输入的检查应包括它们接受的每种“类型”,而输出的检查应准确包含它们“返回”的内容

接受一种类型

在最基本的情况下,您需要创建一个“接受”或“返回”一种类型的块,您需要将该类型包含在连接的连接检查中。

接受单个类型的值块

接受多种类型

如需创建“接受”多种类型的块,您需要在输入的连接检查中包含每种可接受的类型。

接受多种类型的值块

按照惯例,如果输出有时可在多种情况下接受(例如,如果您允许将数字用作字符串),则输出应更为严格,而输入应更为宽松。此惯例可确保输出在不受支持的位置不会连接。

接受任何类型的

如需创建“接受”任何类型的块,您需要将输入的连接检查设置为 null

接受任何类型的值块

返回子类型

如需创建“返回”子类型的块,您需要在输出的连接检查中同时包含类型和超类型。

返回其类型及其超类型的值块

对于子类型,可以在输出检查中进行多次检查,因为块始终会“返回”这两种类型。

返回参数化类型

如需创建“返回”参数化类型的块,您需要在输出的连接检查中同时包含参数化版本和未参数化版本。

根据您希望块语言的严格程度,您可能还需要添加类型的差异

返回参数化类型及其未参数化类型的值块

与子类型一样,在这种情况下,可以在输出检查中包含多个检查,因为块始终会“返回”这两种类型。

堆栈或语句示例

您可以通过几种常见方式定义针对上一个和下一个连接的检查。通常,您认为这些规则会限制块的顺序。

后续连接应该包括哪些块应该紧跟在当前块之后,以前的连接包括当前块“是”什么。

按顺序排列方块

如需创建一组按指定顺序连接的块,您需要在下一次连接检查中包括哪些块应该跟随当前块,以及上个连接检查中的当前块“是”什么。

强制顺序语句的语句块

允许放置大量中间方块

如需创建一组允许大量中间块的有序块,您需要在中间块的下一次连接检查中,至少包含一个来自中间块的上一次连接检查的条目。这样,可以在代码块之后使用更多本身。

允许放置大量中间块的语句块

不允许中间方块

如需创建一组有序块(其中中间块是可选的),您需要在第一个块的下一次连接检查中,至少包含一个来自中间块的上一次连接检查和最后一个块的上一次连接检查的条目。这样,第一个代码块可以后跟一个中间代码块或最后一个代码块。

不允许中间块的语句块

“两个”或“堆栈”

如需创建只能后跟一个组的块或者只能跟着另一个组的块(但不能同时跟两个组的块)的块,您需要执行以下两项操作:

  1. 在第一个块的下一次连接检查中,您需要在上述两个组的连接检查中包含至少一个条目。

  2. 您需要定义这些组的下一次连接检查,以仅包含其之前的连接检查中的值(这样它们只能跟随同一组的块)。

语句块,后面可跟一种类型的块中的多种,或另一种类型中的多种,但不能同时跟这两种块

限制

该系统非常稳健,可以满足许多用例,但也存在一些限制。

限制范围较大的上下文

此系统本身不支持限制允许连接连接的“更优质的上下文”。例如,您不能说 break 块只能存在于 loop 块内部。连接检查系统仅考虑直接连接的两个连接。

可以使用事件系统监听 块移动事件并检查该块的位置是否正确,从而支持此功能。

Blockly.Blocks['custom_block'] = {
  init: function() { }

  onchange: function(e) {
    if (this.workspace.isDragging()) return;
    if (e.type !== Blockly.Events.BlockMove) return;
    if (!this.getSurroundLoop()) this.outputConnection.disconnect();
  }

  loopTypes: new Set(); // Your valid *block types* (not connection checks).

  getSurroundLoop: function () {
    let block = this.getSurroundParent();
    do {
      if (loopTypes.has(block.type)) return block;
      block = block.getSurroundParent();
    } while (block);
    return null;
  },
}

泛型类型

此系统本身不支持定义泛型类型。例如,您不能创建一个“Identity”块来“返回”任何输入。

您可以通过主动更改对块输出的连接检查以匹配其输入,在一定程度上支持这一点。您可以使用事件系统来监听屏蔽移动事件。

Blockly.Blocks['custom_block'] = {
  init: function() { }

  onchange: function(e) {
    if (e.type !== Blockly.Events.BlockMove) return;
    this.setOutput(
        true, this.getInputTargetBlock()?.outputConnection.getCheck());
  }
}

但是,如果连接块也是通用块,则无法正常运行。对于这种情况,目前还没有很好的解决方法。

连接检查工具

如果此系统不适用于您的使用场景,您还可以通过创建自定义连接检查工具来更改连接检查的比较方式。

例如,如果您想创建一个更高级的系统来处理此系统的某些限制,您可以创建自定义连接检查工具。