Aprajafalva csatornahálózata (folytatás)

Használható segédanyagok: Haskell könyvtárainak dokumentációja, (lokális!) Hoogle, a tárgy honlapja és a BE-AD rendszerbe feltöltött beadandók. Ha bármilyen kérdés, észrevétel felmerül, azt a felügyelőknek kell jelezni, nem a diáktársaknak!

A feladatok egymásra épülnek, ezért érdemes ezeket a megadásuk sorrendjében megoldani, de legalább megérteni az aktuális feladatot megelőző feladatokat! A függvények definíciójában lehet, sőt javasolt is alkalmazni a korábban definiált függvényeket (függetlenül attól, hogy sikerült-e azokat megadni).

Tekintve, hogy a tesztesetek, bár odafigyelés mellett íródnak, nem fedik le minden esetben a függvény teljes működését, határozottan javasolt még külön próbálgatni a megoldásokat beadás előtt, vagy megkérdezni a felügyelőket!

Részpontszámokat csak az elégséges szint elérése után lehet kapni!

A feladat összefoglaló leírása

Aprajafalva csatornahálózata az évek során eléggé kapkodva készült el, komolyabb tervezés sosem volt eddig. De most elkészültek az első tervek egy teljesen új hálózat megépítésére, amely terven azonban sajnos maradtak még hibák, hiányosságok. A következőkben ezeket fogjuk ellenőrizni és javítani.

A csatornahálózatot szakaszok alkotják, amellyekkel mindig közvetlenül két csomópontot kötünk össze. Egy csomópontot mindig annak a törpnek a nevével azonosítunk, amelynek a közelében található. Ezeket az elnevezéseket és definíciókat fogalmazzuk meg az alábbi típusszinonimákkal:

type Joint   = String
type Section = (Joint, Joint)

Annyit tudunk még a tervről, hogy nem teljesen dilettánsok készítették, így szerencsére köröket nem tartalmaznak!

Példák

Tekintsünk néhány példát a papírra vetett tervekre:

SmurfVillage277cf282eed53ff73dffe6a677b571f06.png

plan1 :: [Section]
plan1 = [ ("Almoska", "Torpilla"), ("Dulifuli", "TorpDerito")
        , ("Epitorpesz", "Dulifuli"), ("Hami", "Trefi")
        , ("Torpilla", "TorpDerito"), ("Trefi", "Torpilla")]

SmurfVillage296b6025fd241be4a718fe3ed7d6f3911.png

plan2 :: [Section]
plan2 = [ ("Almoska", "Torpilla"), ("Dulifuli", "TorpDerito")
        , ("Epitorpesz", "Dulifuli"), ("Hami", "Trefi")
        , ("Torpilla", "Dulifuli"), ("Torpilla", "TorpDerito")
        , ("Trefi", "Torpilla")]

A kezdeti terv elkészítése (1 pont)

A tervként kapott szakaszok listáját alakítsuk egy hálózattá! Ehhez elsőként vezessük be az alábbi típusszinonimákat:

type Capacity = Int
type Network  = [(Joint, Joint, Capacity)]

Tehát a hálózat szakaszokból épül fel, egy szakasz a kezdő és vég csomópontokból, és kapacitásból áll.

Az átalakítás menete, hogy a listában levő összes szakaszt kiegészítjük egy kapacitásra vonatkozó információval. Kezdetben ez folyamatosan 1 legyen!

initialPlan :: [Section] -> Network

Test>
[("Almoska", "Torpilla", 1), ("Dulifuli", "TorpDerito", 1), ("Epitorpesz", "Dulifuli", 1), ("Hami", "Trefi", 1), ("Torpilla", "TorpDerito", 1), ("Trefi", "Torpilla", 1)] :: [(Joint, Joint, Capacity)]
Test>
[("Almoska", "Torpilla", 1), ("Dulifuli", "TorpDerito", 1), ("Epitorpesz", "Dulifuli", 1), ("Hami", "Trefi", 1), ("Torpilla", "Dulifuli", 1), ("Torpilla", "TorpDerito", 1), ("Trefi", "Torpilla", 1)] :: [(Joint, Joint, Capacity)]

Statisztika készítése a csomópontokról (3 pont)

Szeretnénk statisztikákat készíteni a megtervezett csatornahálózathoz, melyben minden csomóponthoz egy rendezett párt rendelünk, amelyek értéke rendre az adott csomópontba befutó, illetve onnan kiinduló szakaszok darabszáma. Ezeket a következő típusszinonimák segítségével tudjuk leírni:

type InSection  = Int
type OutSection = Int
type Statistics = [(Joint, (InSection, OutSection))]

Első lépésként készítsünk egy olyan függvényt, amely egy már meglevő, félkész statisztikát tud frissíteni egy újabb elemmel.

A frissités az alábbiak szerint történik:

Megjegyzés: Először érdemes először vizsgálni, hogy beszúrás vagy frissítés lesz, és a frissítést egy külön segédfüggvénnyel megoldani.

addStatistics :: (Joint, (InSection, OutSection)) -> Statistics -> Statistics

Test>
[("Torpilla", (1, 0))] :: [(Joint, (InSection, OutSection))]
Test>
[("Torpilla", (3, 3))] :: [(Joint, (InSection, OutSection))]
Test>
[("Torpilla", (2, 1)), ("Trefi", (0, 1))] :: [(Joint, (InSection, OutSection))]

Statisztika készítése a teljes hálózatról (2 pont)

Az előzőek alapján készítsük el a teljes csatornahálózat statisztikáját, melyben minden csomóponthoz szerepel egy, a korábbiakból már ismert (Joint, (InSection, OutSection)) típusú érték, tárolva az adott csomópont bejövő, illetve kimenő szakaszainak darabszámát.

networkStatistics :: Network -> Statistics

Test>
[("Almoska", (0, 1)), ("Dulifuli", (1, 1)), ("Epitorpesz", (0, 1)), ("Hami", (0, 1)), ("TorpDerito", (2, 0)), ("Torpilla", (2, 1)), ("Trefi", (1, 1))] :: [(Joint, (InSection, OutSection))]
Test>
[("Almoska", (0, 1)), ("Dulifuli", (2, 1)), ("Epitorpesz", (0, 1)), ("Hami", (0, 1)), ("TorpDerito", (2, 0)), ("Torpilla", (2, 2)), ("Trefi", (1, 1))] :: [(Joint, (InSection, OutSection))]

A hálózat kimeneti pontjai (1 pont)

Keressük meg a csatornahálózat lefolyási pontjait! Azokat a csomópontokat tekintjük ilyennek, amelyekből egyetlen szakasz sem vezet ki.

Megjegyzés: Ez a kiszámított statisztikák már könnyen kiolvasható.

drains :: Network -> [Joint]

Test>
["TorpDerito"] :: [Joint]
Test>
["TorpDerito"] :: [Joint]
Test>
["Folyopart", "TorpDerito"] :: [Joint]

Helyes-e a terv? (1 pont)

Egy tervet akkor tartunk helyesnek, amennyiben:

isValid :: Network -> Bool

Test>
True :: Bool
Test>
False :: Bool
Test>
False :: Bool

Ugyanazon csomópontba beérkező szakaszok (3 pont)

Egy helyes csatornahálózaton szeretnénk adatokat gyűjteni. Azt mondjuk, hogy a j1 csomópont megelőzi a j2 csomópontot a hálózatban, ha j1-ből elindulva a lefektetett szakaszok mentén eljutunk j2-be, fordítva viszont nem.

Egy helyes hálózat és egy k kezdő csomópont esetében határozzuk meg a k és az összes őt megelőző csomópont esetében az adott csomópontot megelőző összes csomópont listáját.

A végeredmény egy [(Joint, [Joint])] típusú érték lesz, ahol minden egyes elem azt írja le, hogy az adott csomópontot mely csomópontok előzik meg.

Segítség: Gondolkozzunk rekurzív megoldásban, ahol, ha egy lépésben már ismert egy csomópont összes megelőzőjéhez a [Joint] érték, akkor ebből az adott csomópont összes megelőzője már könnyen kiemelhető.

incoming :: Network -> Joint -> [(Joint, [Joint])]

Test>
[("Hami", [])] :: [(Joint, [Joint])]
Test>
[("Torpilla", ["Almoska", "Trefi", "Hami"]), ("Almoska", []), ("Trefi", ["Hami"]), ("Hami", [])] :: [(Joint, [Joint])]
Test>
[("Dulifuli", ["Epitorpesz"]), ("Epitorpesz", [])] :: [(Joint, [Joint])]
Test>
[("Dulifuli", ["Epitorpesz", "Torpilla", "Almoska", "Trefi", "Hami"]), ("Epitorpesz", []), ("Torpilla", ["Almoska", "Trefi", "Hami"]), ("Almoska", []), ("Trefi", ["Hami"]), ("Hami", [])] :: [(Joint, [Joint])]
Test>
[("TorpDerito", ["Dulifuli", "Epitorpesz", "Torpilla", "Almoska", "Trefi", "Hami"]), ("Dulifuli", ["Epitorpesz"]), ("Epitorpesz", []), ("Torpilla", ["Almoska", "Trefi", "Hami"]), ("Almoska", []), ("Trefi", ["Hami"]), ("Hami", [])] :: [(Joint, [Joint])]
Test>
[("Folyopart", ["Epitorpesz"]), ("Epitorpesz", [])] :: [(Joint, [Joint])]

A kapacitási terv elkészítése (2 pont)

Rendeljünk a hálózat minden szakaszához egy kapacitásértéket, amely a teljes hálózat szempontjából a legjobb működést fogja biztosítani! Egy adott szakasz optimális kapacitása a szakasz kezdő csomópontját megelőző csomópontok darabszáma + 1. Használjuk az előzőleg készített incoming függvényt ennek meghatározására!

capacityPlan :: Network -> Network

Test>
[("Almoska", "Torpilla", 1), ("Dulifuli", "TorpDerito", 2), ("Epitorpesz", "Dulifuli", 1), ("Hami", "Trefi", 1), ("Torpilla", "TorpDerito", 4), ("Trefi", "Torpilla", 2)] :: Network
Test>
⊥₁ :: Network
⊥₁: capacityPlan: Cannot be calculated
CallStack (from HasCallStack):
  error, called at ./SmurfVillage2.lhs:271:21 in main:SmurfVillage2
Test>
⊥₁ :: Network
⊥₁: capacityPlan: Cannot be calculated
CallStack (from HasCallStack):
  error, called at ./SmurfVillage2.lhs:271:21 in main:SmurfVillage2

Pontozás (elmélet + gyakorlat)