Sprawdzanie połączenia

Sprawdzanie połączeń ogranicza możliwość połączenia ze sobą (a tym samym bloków) połączeń.

Sprawdzanie połączeń przydaje się w przypadku typów modelowania. Na przykład te 3 bloki nie są połączone, ponieważ reprezentują kod, który zwraca różne typy:

Pusty blok listy połączony z blokiem pierwiastka kwadratowego
połączony z blokiem pisanym wielkimi literami

Sprawdzanie połączeń może zapobiec blokowaniu połączeń przez te blokady. Dzięki temu użytkownicy od razu otrzymują informacje zwrotne i mogą uniknąć wielu prostych pomyłek.

Działanie

Każde połączenie może być powiązane ze sprawdzaniem połączenia w postaci tablicy ciągów znaków dopuszczającej wartość null.

Dwa połączenia można połączyć, jeśli:

  1. Są zgodne typy (np. dane wyjściowe łączące się z wejściem).
  2. Ich wspólne sprawdzanie połączenia ma co najmniej 1 ciąg znaków.

Na przykład te 2 testy mogą się ze sobą łączyć, ponieważ mają ten sam ciąg znaków 'apple':

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

Nie udało się jednak połączyć tych 2 testów, ponieważ nie mają wspólnych ciągów:

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

Jest jeszcze jeden szczególny przypadek. Jeśli dowolna tablica ma wartość null, te 2 połączenia również mogą się łączyć. Pozwoli Ci to zdefiniować połączenia, które mogą łączyć się z każdym miejscem.

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

Ustaw sprawdzanie

Domyślnie wszystkie połączenia mają kontrolę połączenia null, co oznacza, że mogą się połączyć ze wszystkim. Testy połączeń należy przypisać ręcznie.

Sposób przypisywania kontroli połączeń do połączeń różni się w zależności od tego, czy używasz definicji bloków JSON czy definicji bloków JavaScript.

JSON

W przypadku połączeń najwyższego poziomu przypisujesz sprawdzanie bezpośrednio do właściwości, która określa połączenie. Możesz przypisać wartość null, ciąg znaków (który staje się jedynym wpisem podczas sprawdzania połączenia) lub tablica ciągów znaków.

{
  'type': 'custom_block',

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

W przypadku danych wejściowych możesz przypisać kontrolę do właściwości check definicji danych wejściowych. Jeśli właściwość check nie istnieje, kontrola jest uznawana za null. Przypisana wartość może być ciągiem tekstowym lub tablicą ciągów znaków.

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

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

JavaScript

W przypadku połączeń najwyższego poziomu możesz przekazać sprawdzanie bezpośrednio metody, która je definiuje. Jeśli nie prześlesz wartości, czek będzie uznawany za null. Przekazywana wartość może być ciągiem znaków (który staje się jedynym wpisem podczas sprawdzania połączenia) lub tablicą ciągów znaków.

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']);
  }
}

W przypadku danych wejściowych możesz przekazać sprawdzanie metody setCheck po ich zdefiniowaniu. Jeśli metoda setCheck nie zostanie wywołana, kontrola będzie uznawana za null. Przekazywana wartość może być ciągiem tekstowym lub tablicą ciągów znaków.

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

Wbudowane ciągi kontrolne

Wbudowane bloki sprawdzają połączenia z wartościami 'Array', 'Boolean', 'Colour', 'Number' i 'String'. Jeśli chcesz, aby bloki współdziałały z wbudowanymi blokami, możesz używać tych wartości do zapewnienia ich zgodności.

Przykłady wartości

Gdy definiujesz sprawdzanie połączeń pod kątem danych wejściowych i wyjściowych, zwykle traktuj je jako reprezentujące typy.

Kontrole danych wejściowych powinny obejmować każdy akceptowany typ, a mechanizmy kontroli wyników powinny zawierać dokładnie to, co „zwracają”.

Akceptuj jeden typ

W najbardziej podstawowym przypadku, gdy chcesz utworzyć blok, który „akceptuje” lub „zwraca” jeden typ, musisz go uwzględnić podczas sprawdzania połączenia.

blok wartości, który akceptuje jeden typ

Akceptuj wiele typów

Aby utworzyć blok, który „akceptuje” wiele typów, podczas sprawdzania połączenia danych wejściowych musisz uwzględnić każdy akceptowany typ.

blok wartości, który akceptuje wiele typów

Zgodnie z konwencją, jeśli dane wyjściowe mogą czasem być akceptowane w wielu sytuacjach (np. jeśli zezwalasz na używanie liczb jako ciągów tekstowych), dane wyjściowe powinny być bardziej restrykcyjne, a dane wejściowe mniej restrykcyjne. Dzięki tej konwencji dane wyjściowe nie są połączone w miejscach, w których nie są obsługiwane.

Akceptuj dowolny typ

Aby utworzyć blok, który „akceptuje” dowolny typ, musisz ustawić sprawdzanie połączenia wejścia na null.

blok wartości, który akceptuje dowolny typ

Zwracane podtypy

Aby utworzyć blok, który „zwraca” podtyp, podczas sprawdzania połączenia danych wyjściowych musisz uwzględnić zarówno typ, jak i nadtyp.

blok wartości, który zwraca swój typ i jego nadtyp

W przypadku podtypów można stosować wiele testów w wynikach, bo blok zawsze „zwraca” oba typy.

Zwracane typy z parametrami

Aby utworzyć blok, który „zwraca” typ z parametrami, musisz podczas sprawdzania połączenia w danych wyjściowych uwzględnić zarówno wersję z parametrami, jak i wersję bezparametrową.

W zależności od tego, jak bardzo rygorystyczny ma być język blokowania, możesz też uwzględnić wariancje tego typu.

blok wartości, który zwraca typ z parametrami i typ bez parametrów

Tak jak w przypadku podtypów, w tym przypadku można stosować wiele elementów kontrolnych w wynikach, ponieważ blok zawsze „zwraca” oba typy.

Przykłady stosu lub instrukcji

Deweloperzy mogą określać sprawdzanie poprzednich i następnych połączeń na kilka typowych sposobów. Zwykle przypomina to ograniczanie kolejności bloków.

Następne połączenia powinny zawierać listę bloków, które powinny następować po obecnej, a wcześniejsze połączenia – informacje o tym, jaka jest bieżąca blokada.

Ułóż bloki w kolejności

Aby utworzyć zestaw bloków łączących się w określonej kolejności, w kolejnym sprawdzaniu połączenia musisz określić, które bloki powinny następować po obecnej, a także jak wygląda bieżący blok podczas poprzedniego sprawdzania połączenia.

bloki instrukcji z wymuszoną kolejnością

Zezwalaj na dużo środkowych bloków

Aby utworzyć zestaw uporządkowanych bloków, które pozwalają na wiele bloków środkowych, należy uwzględnić co najmniej 1 wpis z poprzedniego sprawdzania połączenia bloku środkowego w ramach następnego sprawdzania połączenia bloku środkowego. Dzięki temu blok może być podążający za blokiem.

bloków instrukcji, które pozwalają na tworzenie wielu bloków środkowych

Nie zezwalaj na bloki środkowych

Aby utworzyć zestaw uporządkowanych bloków, w których bloki środkowe są opcjonalne, należy uwzględnić co najmniej 1 wpis zarówno z poprzedniego sprawdzania połączenia bloku środkowego, jak i z pierwszego sprawdzenia połączenia ostatniego bloku w ramach następnego sprawdzania połączenia pierwszego bloku. Dzięki temu po pierwszej bryle może następować albo blok środkowy, albo ostatni.

bloki instrukcji, które nie zezwalają na bloki pośrednie

Stosy typu „albo lub”

Aby utworzyć blok, po którym mogą występować tylko bloki z jednej grupy lub blokady z innej (i nie obie), musisz wykonać 2 czynności:

  1. W następnym sprawdzeniu połączenia w pierwszym bloku musisz uwzględnić co najmniej 1 wpis z obu grup poprzednio sprawdzanych połączeń.

  2. Musisz zdefiniować mechanizmy sprawdzania następnego połączenia tych grup, aby uwzględniały tylko wartości znajdujące się w ramach poprzednich testów połączeń (aby można było śledzić po nich wyłącznie bloki tej samej grupy).

bloki, po których może następować
wiele bloków jednego typu lub wiele innych, ale nie oba te bloki

Ograniczenia

Jest to dość rozbudowany system i obsługuje wiele przypadków użycia, ale ma kilka ograniczeń.

Ogranicz szerszy kontekst

Sam system nie obsługuje ograniczania „większego kontekstu”, w którym można nawiązać połączenie. Nie można na przykład powiedzieć, że blok break może istnieć tylko wewnątrz bloku loop. System sprawdzania połączeń uwzględnia tylko 2 najbliższe połączenia.

Możesz to obsłużyć, używając systemu zdarzeń, który nasłuchuje zdarzeń przesunięcia blokowego i sprawdź, czy blok jest nieprawidłowo umiejscowiony.

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;
  },
}

Rodzaje ogólne

Ten system sam nie obsługuje definiowania typów ogólnych. Nie możesz na przykład utworzyć bloku „Identity”, który „zwraca” niezależnie od tego, jaka jest jego wartość wejściowa.

Możesz to w pewnym stopniu obsługiwać, zmieniając sprawdzanie połączenia w danych wyjściowych bloku, aby pasowały do danych wejściowych. Można to zrobić, korzystając z systemu zdarzeń, by nasłuchiwać zdarzeń przenoszenia.

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

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

Jeśli jednak połączony blok jest również ogólny, nie będzie działać prawidłowo. W tej sytuacji nie ma dobrego rozwiązania.

Narzędzia do sprawdzania połączeń

Jeśli ten system nie sprawdza się w Twoim przypadku, możesz też zmienić sposób porównywania testów połączeń, tworząc niestandardowy moduł do sprawdzania połączeń.

Jeśli na przykład chcesz utworzyć bardziej zaawansowany system, który obsługuje niektóre z tych ograniczeń, możesz utworzyć niestandardowe narzędzie do sprawdzania połączeń.