Kurzusok
/
Crystal
Crystal
/
Feladatok
/
Feladatkezelő
Feladatkezelő

Feladatkezelő

Tanulófeladat

Bevezetés

A blokk egy kódrészlet, amelyet át lehet adni egy metódusnak. Ez más programozási nyelvekben névtelen függvény vagy lambda néven ismert. Egy metódus a blokkot Proc típusúvá alakíthatja, ami egy kódrészletet képvisel.

def my_method(&block)
  1 + yield
end

my_method { 2 }
# => 3

A blokkok és a Proc-ok nagyon gyakoriak a Crystalben, például iterációhoz és visszahívásokhoz. De arra is használják őket, hogy DSL-eket (domain-specifikus nyelveket) hozzanak létre; a DSL-ek néhány példája a Kemal webes keretrendszer vagy a Spec tesztelési keretrendszer (amit a Crystal megoldásaid tesztelésére használnak).

Blokkok

Ahhoz, hogy olyan metódust hozz létre, amely blokkot fogadhat, a legutolsó argumentum elé kell tenned az és-jelet (&). Ez a blokkot Proc típusúvá alakítja, és az argumentumhoz rendeli. Ezután a yield használatával végrehajtod a metódusnak átadott blokkot.

def my_method(&block)
  1 + yield
end

Blokkot kétféleképpen adhatsz át: a do ... end kulcsszavakkal vagy kapcsos zárójelekkel ({}). A kettő közötti fő különbség a precedencia: a do ... end támogatja a többsoros blokkokat, a kapcsos zárójelek ({}) pedig nem.

my_method do
  2
end
# => 3

my_method { 2 }
# => 3

Yield

yield egy kulcsszó, amely végrehajtja a metódusnak átadott blokkot. Ez azt jelenti, hogy a blokk soha nem hajtódik végre, ha nem hívod meg a yield-et. A yield argumentumokat is kaphat, amelyeket átad a blokknak. Nincs korlátja annak, hány argumentumot adhatsz át a blokknak. A blokkban lévő argumentumok használatához a |x, y, ...| szintaxist használod. Itt x, y, ... az argumentumok nevei.

def my_method(&block)
  1 + yield 2
end

# With curly braces:

my_method { |x| x * 3 }
# => 7

# With do ... end:

my_method do |x|
  x * 3
end
# => 7

A fenti példában a blokk megkapja a 2 argumentumot, megszorozza 3-mal, és 6-ot ad vissza. Ezután a metódus hozzáad 1-et az eredményhez, és 7-et ad vissza.

Ha a yield-nek adott argumentumok száma több, mint amennyit a blokk elvár, hiba keletkezik.

def my_method(&block)
  1 + yield
end

my_method { |x| x * 3 }
# Error: too many block parameters (given 1, expected maximum 0)

A blokkok típusai

A blokkokat megírhatod explicit vagy implicit módon. Az implicit blokkok a leggyakoribbak, és a fenti példákban is ilyeneket látsz. Sőt, a blokknak különböző típusú argumentumokat is adhatsz, és a fordító kikövetkezteti a típusokat, majd uniótípust hoz létre.

def my_method(&block)
  yield "a"
  yield 1
end

my_method do |x|
  p typeof(x)
end

# output: (String | Int32)

Explicit blokkok írásakor megadhatod az argumentumok típusát és a visszatérési típust. Erre az Arg1Type, Arg2Type, ... -> ResultType szintaxis szolgál. Itt az argumentumokat vesszők (,) választják el, az eredménytípust pedig a -> választja el.

def my_method(&block : Int32, Int32 -> Int32)
  1 + yield(2, 3)
end

my_method { |x, y| x * y }
# => 7

Ahogy más explicit típusoknál, a fordító itt is kikényszeríti, hogy a típusok megfeleljenek az elvártnak. Ha a blokk nem egyezik a metódusnak megadott típussal, hiba keletkezik.

Proc-ok

A Proc nagyon szorosan kapcsolódik a blokkokhoz. A fő különbség, hogy a Proc egy típus, a blokk pedig nem. A Proc egy függvénypointert képvisel, és változóként továbbadható.

Proc definiálásához a ->(args) { ... } proc literál szintaxist használod. Az argumentumokat vesszők (,), a blokkot pedig { ... } választja el. Egy Proc meghívásához a call metódust használod, és átadod neki az argumentumokat. A paraméterek típusai kötelezőek. A visszatérési típus a proc törzséből következtethető ki, de explicit módon is megadható.

my_proc = ->(x : Int32, y : Int32) : Int32 { x * y }
my_proc.call(2, 3)
# => 6

Blokkok befogása

A blokkok befogása egy módja annak, hogy Proc-ot hozz létre egy blokkból. Ezt úgy csinálod, hogy a metódus utolsó argumentuma elé teszed a & operátort. Ez a blokkot Proc típusúvá alakítja, és az argumentumhoz rendeli.

def my_method(&block)
  block.call
end

my_method { 1 + 2 }
# => 3

Proc-ok átadása metódusoknak

A Proc-ok átadhatók olyan metódusoknak, amelyek blokkot várnak. Ez akkor lehet hasznos, ha valamilyen feltételtől függően különböző blokkokat szeretnél átadni.

def my_method(&block)
  yield 5
end

my_proc = -> (x) { x * 2 }
my_method(&my_proc)
# => 10

Változók típusának deklarálása

Változó deklarálásakor a : Type szintaxissal adhatod meg a változó típusát. Proc-ok esetében a Proc((ArgType, ...), ResultType) szintaxist használod. Az utolsó típus mindig a visszatérési típus, a többi pedig az argumentumtípusok. Ez azt jelenti, hogy ha csak egyetlen típus van, az a visszatérési típus, és a proc nem fog argumentumot kapni.

my_proc : Proc(Int32, Int32, Int32) = ->(x, y) { x * y }
other_proc : Proc(Int32) = -> { 1 }

Utasítások

Egy olyan weboldal tartalomkezelő rendszerét fejleszted, amely cikkeket, blogbejegyzéseket és más írott tartalmakat szolgál ki. A feladatod, hogy megvalósíts egy feladatkezelőt, amely egy prioritási sorban lévő feladatokat dolgoz fel. A feladatkezelő feladatokat kap, és a szerver terhelése, valamint a feladatok prioritása alapján dönti el, hogy melyiket hajtsa végre következőként. Ha a szerver nagy terhelés alatt áll, akkor a feladat végrehajtásához magasabb prioritási számra van szükség. A szerver dinamikusan változtatja a terhelést a terhelés alapján.

1. Inicializáld a TaskHandler-t

A TaskHandler-t inicializálni kell egy sorral és egy alapértelmezett load_level-lel.

Valósítsd meg a TaskHandler osztály inicializálóját, amely argumentumként a feladatnevek String-ekből álló Array-jét veszi át, és egy @tasks nevű példányváltozóban tárolja. Az inicializálónak létre kell hoznia a @task_condition_logic példányváltozót is, amely egy Proc-ot tárol, ami egy Int32-t vesz át, és igazat ad vissza, ha az argumentum egyenlő 0-val vagy nagyobb nála, hamisat pedig, ha kisebb 0-nál.

task_handler = TaskHandler.new(["task1", "task2", "task3"])
# => #<TaskHandler:0x7fd8d4cfbf00 @tasks=["task1", "task2", "task3"], @task_condition_logic=#<Proc(Int32, Bool):0x562536a4a330>>

2. Frissítsd a feladatfeltétel logikáját

A szervernek képesnek kell lennie frissíteni a feladatfeltétel logikáját a szerver terhelése alapján.

Valósítsd meg a TaskHandler#update_task_condition_logic metódust, amely argumentumként egy blokkot vesz át, és a blokkot a @task_condition_logic példányváltozóban kell tárolni.

task_handler = TaskHandler.new(["task1", "task2", "task3"])
task_handler.update_task_condition_logic { |load| load > 1 }
# => #<TaskHandler:0x7f068118cf00 @tasks=["task1", "task2", "task3"], @task_condition_logic=#<Proc(Int32, Bool):0x562df99d0370>>

3. Hajtsd végre a következő feladatot

A szervernek képesnek kell lennie végrehajtani a sorban következő feladatot.

A @tasks tömb első feladata a következő végrehajtandó feladat. A feladatot végre kell hajtani, ha a megadott prioritási szint igazat ad vissza, amikor átadjuk a @task_condition_logic proc-nak.

Attól függően ad vissza üzeneteket, hogy a feladatot végrehajtotta-e vagy sem. Ha a feladatot végrehajtotta, ezt kell visszaadnia: "Completed <task>". Ha a feladatot nem hajtotta végre, ezt kell visszaadnia: "Could not complete <task>".

Ha a feladatot végrehajtotta, el kell távolítani a @tasks tömbből.

Valósítsd meg a TaskHandler#execute_next_task metódust, amely argumentumként a következő feladat prioritási szintjét veszi át Int32-ként, és attól függően ad vissza egy String-et, hogy a feladatot végrehajtotta-e vagy sem.

task_handler = TaskHandler.new(["task1", "task2", "task3"])
task_handler.update_task_condition_logic { |load| load > 1 }
task_handler.execute_next_task(2)
# => "Completed task1"

task_handler.execute_next_task(1)
# => "Could not complete task2"
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
Crystal Exercism

Készen állsz elkezdeni a(z) Feladatkezelő feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Crystal nyelvet 26 fogalom142 feladat segítségével, valódi emberi mentorálással, mindez ingyen.