Kilka słów o funkcji IF w Dynamo

Czym różni się programowanie imperatywne (Python) od asocjacyjnego (Dynamo) i co wynika z tej różnicy podczas tworzenia naszych skryptów.

Zadanie

W praktyce projektowej spotykamy się często w różnych przepisach lub wytycznych projektowych z zaleceniami zależnymi od jakiegoś parametru. Jeżeli coś jest mniejsze niż A, wybierz B, jeśli większe lub równe A, wybierz C. Spróbujmy ułatwić sobie naszą pracę automatyzując proces doboru takiego parametru do zadanych warunków.

Algorytm w języku Python

Kod pętli

Rozważmy taki pseudokod (zachowana jest składnia języka Python, jednak dla ułatwienia naszego zadania pojawia się pseudoklasa Rura):

for rura in zestawienie_rur:
    if rura.srednica < 30
        rura.przeswit = rura.srednica + 20
    else:
        rura.przeswit = rura.srednica + 10

Przedstawiona pętla for wykonuje tą samą czynności dla każdego elementu zbioru. Tutaj tym zbiorem jest instalacja sanitarna. Funkcja if podczas każdej iteracji sprawdza, czy warunek średnicy mniejszej niż 30 jednostek jest spełniony dla rury wybranej podczas danego przebiegu pętli. Jeżeli tak, to ustalamy parametr “prześwitu” dla niej jako sumy średnicy i 20 jednostek. W innym wypadku (else) program dobiera prześwit dla sumy średnicy i 10 jednostek.

Programowanie imperatywne

Wzorzec tworzenia oprogramowania, w którym wykonywane po kolei instrukcje zmieniają stan programu to programowanie imperatywne. I taki jest powyższy kod – zabierz, sprawdź, jeżeli wynik jest taki to zabeirz tyle, itd.. Wskazujemy komputerowi wykonującemu nasz program dokładne “miejsce i czas” wykonania instrukcji.

Dynamo, czyli programowanie asocjacyjne

Program utworzony w paradygmacie asocjacyjnym, – w dużym uproszczeniu – będzie cały czas utrzymywał w swojej pamięci wszystkie zależności między zmiennymi (lub wszystkie zależności grafu w wypadku programowania wizualnego) i równolegle będzie na nich wszystkich operował.

Takie zachowanie najłatwiej jest zauważyć przy pracy z listami. Python wymaga korzystania z wyrażeń listowych (list comprehension), natomiast dla DesignScript nie ma znaczenia czy dla operacji np. dodawania, argumentem jest lista, czy pojedyncza zmienna.

>>> l = [1, 2, 3]
>>> print(l)
[1, 2, 3]
>>> l = [a+2 for a in l]
>>> print(l)
[3, 4, 5]

Strategie tworzenia grafów w Dynamo

Dla pokazania trzech możliwych strategii rozwiązania zadania ze wstępu posłużymy się zestawem 12 rur.

Przykładowa lista rur dla których będziemy wykonywać obliczenia
Przykładowa lista rur dla których będziemy wykonywać obliczenia

Rozdzielenie listy na dwie osobne gałęzie

Graficzne rozdzielenie listy - cały graf.
Graficzne rozdzielenie listy – cały graf

Takie rozwiązanie najczęściej spotykamy u początkujących adeptów Dynamo (ponieważ jest ono dość intuicyjne). Grupa oznaczona cyfrą 1 służy do rozdzielenia naszej listy na dwie osobne znajdujące się w dwóch osobnych strumieniach. Wykorzystanie węzła List.Filter jest o tyle ciekawym zabiegiem, że posiada on dwa wyjścia, dzięki którym nie musimy rozdzielać wyniku z węzła ręcznie (np. za pomocą Lists.GetItemAtIndex)

Rozdzielenie listy na dwa osobne strumienie
Rozdzielenie listy na dwa osobne strumienie

W grupie 2a i 2b (obie są identyczne poza polem z wartością o którą powiększamy średnicę – 20 lub 10) dokonujemy obliczenia wartości dla parametru Prześwit.

Obliczenie wartości dla parametru Prześwit
Obliczenie wartości dla parametru Prześwit

Ostatnia, podwojona grupa (3a i 3b) służy do zapisania obliczonego parametru do elementu w przestrzeni modelu.

Ustalenie parametru Prześwit dla elementu modelu.
Ustalenie parametru Prześwit dla elementu modelu

Zaletami takiego rozwiązania są:

  • Czytelność dla innych użytkowników skryptu, w szczególności dla osoby potrzebującej dostosować skrypt do innych warunków.
  • Możliwość wprowadzania różnych sposobów obliczeń w zależności od parametru (nie zawsze będziemy tylko dodawać liczby)

Wadą takiego rozwiązania (gdy tak jak w naszym algorytmie zmienią się tylko wartość liczby, którą dodajemy) jest uciążliwe zarządzanie nim i ewentualne zmiany, które musimy powielić dla każdej gałęzi grafu.

Logiczne rozdzielenie listy

Logiczne rozdzielenie listy
Logiczne rozdzielenie listy

Kolejna strategia, to rozwinięcie poprzedniego sposobu. Polega ono na zagnieżdżeniu dwóch list w jednej głównej i wykorzystaniu sposobu skratowania jeden do wielu.

Zagnieżdżanie list według funkcji
Zagnieżdżanie list według funkcji

Węzły grupy 2 odczytują parametr Średnica dla każdego elementu z obu list, a w grupie 3 jest on powiększany o odpowiednio 10 i 20 jednostek w zależności od kolejności list.

Odczytanie i obliczenie parametru w zależności od list
Odczytanie i obliczenie parametru w zależności od list

Grupa 4 wygląda analogicznie do grupy 3 z poprzedniego przykładu.

Takie podejście ogranicza nam złożoność grafu i ułatwia jego zarządzanie. Warunkiem jest jednak wykonanie tej samej operacji (dodawania) niezależnie od parametru na którym mamy operować.

Jednak wadą takiego rozwiązania jest mniejsza czytelność. Warto jest zostawić notatkę, która tłumaczy sposób działania skryptu.

Wykorzystanie węzła if

Przykład użycia węzła if do doboru parametru
Przykład użycia węzła if do doboru parametru

Dynamo posiada węzeł if, jednak specyfika jego zachowania jest inna od tej, do której jesteśmy przyzwyczajeni w języku Python – dlaczego tak jest, to materiał na cały osobny artykuł. W prezentowanym przykładzie sprawdzany jest przekrój rury pod kątem naszego warunku. Węzeł < tworzy listę zawierającą wartości “true” i “false”, a węzeł if wybiera w zależności od nich liczbę 20 lub 10. Tą z kolei dodajemy do wartości odczytanych z parametru Średnica.

Przykład użycia węzła if do doboru parametru.
Przykład użycia węzła if do doboru parametru.

Ta strategia jest bardziej czytelna i zwięzła w porównaniu do zaprezentowanych dwóch poprzednich, jednak najmniej uniwersalna. Działa tylko w wypadku warunku rozkładającego się na dwa przypadki i wykonującego to samo działanie. Różnić mogą się tylko parametry.

Punkty wspólne

Podsumowując, to co łączy te trzy podejścia to rozłożenie ścieżki grafu na gałęzie. To rozłączenie pojawia się w różnych miejscach. W pierwszej wersji rozdzielane są elementy i operacje dla wszystkich gałęzi wykonywane są osobno. Strategia z drugiego podejścia rozdzielamy elementy do zagnieżdżonych list. W trzecim rozwiązaniu powielamy listę i ją modyfikujemy (otrzymując listę wartości), aby połączyć ją w węźle ustalającym parametry dla obiektów w modelu. I to jest największa różnica w porównaniu do języka Python. W nim, cały czas operujemy na jednej liście, a w pętli for tylko modyfikujemy jej elementy.

Prezentowane strategie są równoprawne względem siebie i mają zastosowanie dla różnych przypadków. To właśnie oczekiwane działanie skryptu zadecyduje o wyborze jednej z nich.

Tryb imperatywny jest możliwy w DesignScript

Na zakończenie – aby móc z czystym sumieniem zamknąć omawianie trybu imperatywnego i asocjacyjnego w Dynamo (w DesignScript), należy powiedzieć, że DesignScript obsługuje oba tryby. Warunkiem jest wskazanie środowisku, że właśnie w takim trybie ma działać. Trudnością dla użytkownika może być konieczność rozumienia składni tego języka oraz jego instrukcji.

Tryb imperatywny w Dynamo
Tryb imperatywny w Dynamo
geometria = [Imperative]
{
    lista = 0..10;
    punkty = [];

    for (i in lista)
    {
        punkty[i] = Point.ByCoordinates(i, 0, 0);
    }

    return = punkty;
};

Tags:

Zobacz podobne

Opcje sterowania wyborem elementów w Autodesk Revit

Czasami zaznaczenie elementów w oprogramowaniu Autodesk Revit jest niemożliwe. Zwykle przyczyną jest włączenie jednej z kilku ...

Podrysy i półcienie

W wielu przypadkach zakres widoczności widoku jest niewystarczający do wygenerowania kompletnej dokumentacji. Typowym przykładem jest potrzeba ...

Zaokrąglenia w Revicie …

W ostatnim czasie wiele osób zgłaszało problemy z zaokrągleniem w Revicie, a dokładniej z zaokrągleniem sum. Przyjrzyjmy się zatem ...