# Run as: iex --dot-iex path/to/notebook.exs # Title: DP25a - 3. FP-gyakorlat, 2025-09-30 # ── Műveletek listákon ── # A 2. FP-gyakorlaton az L1-L4 feladatok már szerepeltek (ott L3-L6 jelöléssel), de sokan idő hiányában nem tudták megoldani. Most ismét feladjuk ezeket is, azzal a kiegészítéssel, hogy minél több magasabb rendű és más könyvtári függvényt használjon. # Írjon többféle megoldást a feladatokra: magasabb rendű és más könyvtári függvényekkel (`Enum.map/2`, `Enum.filter/2`, `Enum.reduce/3`, `List.foldr/3`, , `List.foldl/3` stb.), valamint `for`-komprehenzióval. # Hasonlítsa össze a megoldások futási idejét a `benchee`-vel, próbáljon hatékonyabb kódot írni pl. jobbrekurzióval. # Használja a `kino_explorert`-t nyomkövetésre, hibakeresésre. # Próbálja ki a `dialyxir` modult, azaz a _dialyzer_ segédeszközt, ellenőrizze programjaiban a típus- és függvényspecifikációk helyességét. # A _dialyzert_ nem tudja _Livebook_ cellában futtatni, mert a _dialyzer_ a lefordított BEAM-kódot elemzi, a _Livebook_ pedig nem menti el a BEAM-kódot a háttértárba. Ezért hozzon létre egy _mix_ projektet, másolja ki az elemzendő programrészeket a _Livebook_ cellá(k)ból, és mentse el ezt a kódot a _mix_ projekt fájlrendszerén belül a `lib` mappában egy `.ex` típusnevű fájlba. Írja be a _mix_ függőségei közé a `dialyxir`-t, töltse le és fordítsa le az új modulokat. Ezután már futtathatja a _dialyzert._ A harmadik FP-előadáson néhány dián össze van foglalva a `dialyxir` telepítése és használata. Itt is olvashat róla: `https://hexdocs.pm/dialyxir/readme.html`. # A _mix_ parancssoros használatához az _elixirt_ telepítenie kell a saját gépére, vagy _dockerből_ kell tudni futtatnia. Az első FP-előadás diái segítenek az _elixir_ telepítésben, az _elixir_ és a _mix_ használatban, ha eddig még nem használta parancssorból az _elixirt_ vagy a _mixet._ # ### L1. Lista minden n-edik elemének kihagyásával létrejövő lista # Írjon függvényt egy olyan lista létrehozására, amelyikből a paraméterként átadott lista minden n-edik eleme, a nulladiktól kezdve, ki van hagyva. (A listák indexelése, mint tudjuk, a 0-val kezdődik.) #

# Ne használja az Enum.split*, Enum.take* és Enum.drop* függvények semelyik változatát a dropevery/2 függvény megvalósítására! (De bármilyen könyvtári függvényt használhat az eredmény ellenőrzésére.) #

#
# ```elixir # defmodule Drop do # @spec dropevery(xs :: [any()], n :: integer()) :: rs :: [any()] # def dropevery(xs, n) do # ... # end # end # ls = ~c"álom" ++ [:a] ++ ~c"egybrigád" # IO.inspect(Drop.dropevery(ls, 4) === ~c"lomegyrigd") # ls = ~c"abcdefghijkl" # IO.inspect(Drop.dropevery(ls, 5) === ~c"bcdeghijl") # ls = ~c"1234567" # IO.inspect(Drop.dropevery(ls, 2) === ~c"246") # ls = [] # IO.inspect(Drop.dropevery(ls, 3) === []) # ls = [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m] # IO.inspect(Drop.dropevery(ls, 3) === [:b, :c, :e, :f, :h, :i, :k, :l]) # ``` #
#
# Súgó # Ha nem ír segédfüggvényt és nincs más ötlete, használhatja a for-jelölést, generátoraként a ../2, szűrőjeként a rem/2 függvényeket, a listaelemek elérésére pedig a Enum.at/2 függvényt. #
defmodule Drop1 do @spec dropevery(xs :: [any()], n :: integer()) :: rs :: [any()] def dropevery([], _n), do: [] def dropevery(xs, n) do for i <- 1..length(xs), rem(i, n) != 1, do: Enum.at(xs, i - 1) end end ls = ~c"álom" ++ [:a] ++ ~c"egybrigád" IO.inspect(Drop1.dropevery(ls, 4) === ~c"lomegyrigd") ls = ~c"abcdefghijkl" IO.inspect(Drop1.dropevery(ls, 5) === ~c"bcdeghijl") ls = ~c"1234567" IO.inspect(Drop1.dropevery(ls, 2) === ~c"246") ls = [] IO.inspect(Drop1.dropevery(ls, 3) === []) ls = [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m] IO.inspect(Drop1.dropevery(ls, 3) === [:b, :c, :e, :f, :h, :i, :k, :l]) defmodule Drop2 do @spec dropevery(xs :: [any()], n :: integer()) :: rs :: [any()] def dropevery(xs, n), do: Enum.reverse(dropevery(xs, n, 1, [])) def dropevery([], _n, _i, zs), do: zs def dropevery([x | xs], n, i, zs) when rem(i, n) != 1, do: dropevery(xs, n, i + 1, [x | zs]) def dropevery([_ | xs], n, i, zs), do: dropevery(xs, n, i + 1, zs) end ls = ~c"álom12" ++ [:a] ++ [~c"34brigád"] IO.puts(Drop2.dropevery(ls, 5) == Enum.drop_every(ls, 5)) ls = ~c"abcdefghijkl" IO.inspect(Drop2.dropevery(ls, 4)) ls = ~c"1234567" IO.inspect(Drop2.dropevery(ls, 2)) ls = [] IO.inspect(Drop2.dropevery(ls, 3)) ls = [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m] IO.inspect(Drop2.dropevery(ls, 3)) # ### L2. Lista egyre rövidülő szuffixumainak listája # Írjon olyan függvényt, amely egy xs lista elemeiből álló részlistákat ad eredményül: az első részlista maga az xs legyen, a második részlista az xs második, azaz 1 indexű elemétől a végéig tartson, a harmadik részlista az xs harmadik, azaz 2 indexű elemétől a végéig tartson, # s.í.t., az utolsó részlista az üres lista legyen. #
# ```elixir # defmodule Tails do # @spec tails(xs :: [any()]) :: zss :: [[any()]] # # Az xs lista egyre rövidülő szuffixumainak listája zss # def tails(xs) do # ... # end # end # IO.puts(Tails.tails([1, 4, 2]) === [[1, 4, 2], [4, 2], [2], []]) # IO.puts(Tails.tails([:a, :b, :c, :d]) === [[:a, :b, :c, :d], [:b, :c, :d], [:c, :d], [:d], []]) # IO.puts(Tails.tails([:z]) === [[:z], []]) # IO.puts(Tails.tails([]) === [[]]) # ``` #
#
# Súgó # A tails függvénynek listák listája az eredménye, így ha üres listára alkalmazzuk, akkor olyan lista lesz a visszatérési értéke, melynek egyetlen eleme van, az üres lista. #
defmodule Tails do @spec tails(xs :: [any]) :: zss :: [[any]] # Az xs lista egyre rövidülő szuffixumainak listája zss def tails([]), do: [[]] def tails([_x | xs] = xxs), do: [xxs | tails(xs)] end IO.puts(Tails.tails([1, 4, 2]) === [[1, 4, 2], [4, 2], [2], []]) IO.puts(Tails.tails([:a, :b, :c, :d]) === [[:a, :b, :c, :d], [:b, :c, :d], [:c, :d], [:d], []]) IO.puts(Tails.tails([:z]) === [[:z], []]) IO.puts(Tails.tails([]) === [[]]) # ### L3. Lista egymást követő két-két eleméből képzett párok listája # Írjon olyan rekurzív függvényt, amelyik egy lista 1. és 2., 3. és 4., 5. és 6. s.í.t. elemeiből képzett párok listáját adja eredményül. Ha a listának kettőnél kevesebb eleme van, az eredmény az üres lista legyen. Ha a listának páratlan számú eleme van, az utolsót dobja el. #
# ```elixir # defmodule Pairs do # @spec pairs(xs::[any()]) :: zs :: [any()] # def pairs(xs), do: ... # end # zs = [{1,2}, {3,4}, {5,6}, {7,8}, {9,10}, {11,12}, {13,14}, {15,16}, {17,18}, {19,20}] # (1..20 |> Range.to_list() |> Pairs.pairs() == zs) |> IO.puts # zs = [{1,2}, {3,4}, {5,6}, {7,8}, {9,10}] # (1..11 |> Range.to_list() |> Pairs.pairs() == zs) |> IO.puts # ([1] |> Pairs.pairs() == []) |> IO.puts # ([1] |> Pairs.pairs() == []) |> IO.puts # ``` #
defmodule Pairs do @spec pairs(xs :: [any()]) :: zs :: [any()] def pairs([x1, x2 | xs]), do: [{x1, x2} | pairs(xs)] def pairs(_), do: [] end zs = [{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}] (1..20 |> Range.to_list() |> Pairs.pairs() == zs) |> IO.puts() zs = [{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}] (1..11 |> Range.to_list() |> Pairs.pairs() == zs) |> IO.puts() ([1] |> Pairs.pairs() == []) |> IO.puts() ([1] |> Pairs.pairs() == []) |> IO.puts() # ### L4. Listában párosával előforduló elemek listája # Írjon olyan rekurzív függvényt, amelyik egy lista elemei közül az összes olyat visszaadja az eredménylistában, amelyet vele azonos értékű elem követ, azaz például két egymást követő, azonos értékű elemből egyet, három egymást követőből kettőt stb. Írhat segédfüggvényt és akkumulátort nem használó, valamint akkumulátoros segédfüggvényt használó változatot. Próbáljon meg egyéb változatokat is írni, pl. a for-jelöléssel és az Enum.zip/1 függvény alkalmazásával. #
# ```elixir # defmodule Parosan do # @spec parosan(xs :: [any()]) :: rs :: [any()] # # Az xs lista összes olyan elemének listája rs, amely # # után vele azonos értékű elem áll # def parosan xs do # ... # end # end # IO.puts(Parosan.parosan([:a, :a, :a, 2, 3, 3, :a, 2, :b, :b, 4, 4]) === [:a, :a, 3, :b, 4]) # IO.puts(Parosan.parosan([:a, 2, 3, :a, 2, :b, 4]) === []) # IO.puts(Parosan.parosan([:a]) === []) # IO.puts(Parosan.parosan([]) === []) # ``` #
defmodule Parosan1 do @spec parosan(xs :: [any()]) :: rs :: [any()] # Az xs lista összes olyan elemének listája rs, amely # után vele azonos értékű elem áll def parosan([x | [x | _xs] = xxs]), do: [x | parosan(xxs)] def parosan([_ | [_x | _xs] = xxs]), do: parosan(xxs) def parosan(_), do: [] end IO.puts(Parosan1.parosan([:a, :a, :a, 2, 3, 3, :a, 2, :b, :b, 4, 4]) === [:a, :a, 3, :b, 4]) IO.puts(Parosan1.parosan([:a, 2, 3, :a, 2, :b, 4]) === []) IO.puts(Parosan1.parosan([:a]) === []) IO.puts(Parosan1.parosan([]) === []) defmodule Parosan2 do @spec parosan(xs :: [any()]) :: rs :: [any()] # Az xs lista összes olyan elemének listája rs, amely # után vele azonos értékű elem áll def parosan(xs), do: parosan(xs, []) |> Enum.reverse() @spec parosan(xs :: [any()], zs :: [any()]) :: rs :: [any()] def parosan([x | [x | _xs] = xxs], zs), do: parosan(xxs, [x | zs]) def parosan([_ | [_x | _xs] = xxs], zs), do: parosan(xxs, zs) def parosan(_, zs), do: zs end IO.puts(Parosan2.parosan([:a, :a, :a, 2, 3, 3, :a, 2, :b, :b, 4, 4]) === [:a, :a, 3, :b, 4]) IO.puts(Parosan2.parosan([:a, 2, 3, :a, 2, :b, 4]) === []) IO.puts(Parosan2.parosan([:a]) === []) IO.puts(Parosan2.parosan([]) === []) defmodule Parosan3 do @spec parosan(xs :: [any()]) :: rs :: [any()] # Az xs lista összes olyan elemének listája rs, amely # után vele azonos értékű elem áll def parosan([]), do: [] def parosan(xs) do for {x, y} <- Enum.zip(xs, tl(xs)), x === y, do: x end end IO.puts(Parosan3.parosan([:a, :a, :a, 2, 3, 3, :a, 2, :b, :b, 4, 4]) === [:a, :a, 3, :b, 4]) IO.puts(Parosan3.parosan([:a, 2, 3, :a, 2, :b, 4]) === []) IO.puts(Parosan3.parosan([:a]) === []) IO.puts(Parosan3.parosan([]) === []) # ### L5. Lista elején azonos értékű elemekből álló részlisták listája # Írjon függvényt olyan nemüres, folytonos részlisták előállítására, amelyek egy lista elejétől indulnak, és velük azonos értékű és elemszámú részlisták követik őket. Példák: # `[1,1]` ––> `[[1]]`: A lista elején kezdődő `[1]` részlistát az `[1]` részlista követi. # `[1,1,1]` ––> `[[1]]`: A lista elején kezdődő `[1]` részlistát az `[1]` részlista követi, de a lista elején kezdődő `[1,1]` részlistát már nem követi azonos értékű részlista. # `[1,1,0,0]` ––> `[[1]]`: A lista elején kezdődő `[1]` részlistát az `[1]` részlista követi, de a `[0]` részlista már nem a lista elején kezdődik. # `[1,1,1,1]` ––> `[[1], [1, 1]]`: A lista elején kezdődő `[1]` és `[1, 1]` részlistákat a lista elején kezdődő, azonos értékű részlisták követik. # `[1,1,1,1,1]` ––> `[[1], [1, 1]]`: A lista elején kezdődő `[1]` és `[1, 1]` részlistákat a lista elején kezdődő, azonos értékű részlisták követik, de a lista elején kezdődő `[1, 1, 1]` részlistát már nem követi azonos értékű részlista. # `[1,1,0,1,1]` ––> `[[1]]`: A lista elején kezdődő `[1]` részlistát az `[1]` részlista követi, de nincs több olyan ismétlődő részlista, ami a lista elején kezdődne. # `[1,1,1,1,1,1]` ––> `[[1], [1, 1], [1, 1, 1]]`: A lista elején kezdődő `[1]`, `[1, 1]` és `[1, 1, 1]` részlistákat a lista elején kezdődő, azonos értékű részlisták követik. # Lehetőleg írjon többféle változatot, pl. akkumulátort használó és nem használó változatot, könyvtári függvényeket alkalmazó és nem alkalmazó változatot. #
# ```elixir # defmodule Repeated do # @spec repeated(xs :: [any()]) :: rs :: [any()] # def repeated(xs) do # ... # end # end # (Repeated1.repeated([1,1]) == [[1]]) |> IO.inspect # (Repeated1.repeated([1,1,1]) == [[1]]) |> IO.inspect # (Repeated1.repeated([1,1,0,0]) == [[1]]) |> IO.inspect # (Repeated1.repeated([1,1,1,1]) == [[1], [1, 1]]) |> IO.inspect # (Repeated1.repeated([1,1,1,1,1]) == [[1], [1, 1]]) |> IO.inspect # (Repeated1.repeated([1,1,0,1,1]) == [[1]]) |> IO.inspect # (Repeated1.repeated([1,1,1,1,1,1]) == [[1], [1, 1], [1, 1, 1]]) |> IO.inspect # (Repeated1.repeated([:a, :a, :a, 2, 3, 3, :a, :b, :b, :b, :b]) === [[:a]]) |> IO.inspect # (Repeated1.repeated([:a, :b, :b, :b, :b]) === []) |> IO.inspect # (Repeated1.repeated([:b, :b, :b, :b]) === [[:b], [:b, :b]]) |> IO.inspect # (Repeated1.repeated([]) === []) |> IO.inspect # ``` #
#
# Súgó # Ha nincs jobb ötlete, használja az Enum.take/2 és Enum.drop/2 függvényeket egy segédfüggvényben. #
defmodule Repeated1 do @spec repeated(xs :: [any()]) :: rs :: [any()] def repeated([]), do: [] def repeated(xs), do: repeated(xs, 1) def repeated(xs, i) do cond do Enum.take(xs, i) == Enum.drop(xs, i) |> Enum.take(i) -> [Enum.take(xs, i) | repeated(xs, i + 1)] true -> [] end end end (Repeated1.repeated([1,1]) == [[1]]) |> IO.inspect (Repeated1.repeated([1,1,1]) == [[1]]) |> IO.inspect (Repeated1.repeated([1,1,0,0]) == [[1]]) |> IO.inspect (Repeated1.repeated([1,1,1,1]) == [[1], [1, 1]]) |> IO.inspect (Repeated1.repeated([1,1,1,1,1]) == [[1], [1, 1]]) |> IO.inspect (Repeated1.repeated([1,1,0,1,1]) == [[1]]) |> IO.inspect (Repeated1.repeated([1,1,1,1,1,1]) == [[1], [1, 1], [1, 1, 1]]) |> IO.inspect (Repeated1.repeated([:a, :a, :a, 2, 3, 3, :a, :b, :b, :b, :b]) === [[:a]]) |> IO.inspect (Repeated1.repeated([:a, :b, :b, :b, :b]) === []) |> IO.inspect (Repeated1.repeated([:b, :b, :b, :b]) === [[:b], [:b, :b]]) |> IO.inspect (Repeated1.repeated([]) === []) |> IO.inspect defmodule Repeated2 do @spec repeated(xs :: [any()]) :: rs :: [any()] def repeated([]), do: [] def repeated(xs), do: repeated(xs, 1, []) def repeated(xs, i, zs) do cond do Enum.take(xs, i) == Enum.drop(xs, i) |> Enum.take(i) -> repeated(xs, i + 1, [Enum.take(xs, i) | zs]) true -> Enum.reverse(zs) end end end IO.inspect(Repeated2.repeated([:a, :a, :a, 2, 3, 3, :a, :b, :b, :b, :b]) === [[:a]]) IO.inspect(Repeated2.repeated([:a, :b, :b, :b, :b]) === []) IO.inspect(Repeated2.repeated([:b, :b, :b, :b]) === [[:b], [:b, :b]]) IO.inspect(Repeated2.repeated([]) === []) # ### L6. Listában párosával előforduló részlisták listája # Írjon függvényt egy lista összes olyan nemüres, folytonos részlistájának az előállítására, amelyet vele azonos értékű részlista követ. Lehetőleg írjon többféle változatot. #
# ```elixir # defmodule Stammering do # @spec stammering(xs :: [any()]) :: zss :: [[any()]] # # zss az xs lista összes olyan nemüres, folytonos részlistájából # # álló lista, amelyet vele azonos értékű részlista követ # def stammering(xs) do # ... # end # end # (Stammering.stammering([:a, :a, :a, 2, 3, 3, :a, :b, :b, :b, :b]) === # [[:a], [:a], [3], [:b], [:b, :b], [:b], [:b]]) |> IO.puts() # IO.puts(Stammering.stammering([]) === []) # IO.puts(Stammering.stammering([:a]) === []) # IO.puts(Stammering.stammering([:a, :a]) === [[:a]]) # IO.puts(Stammering.stammering([:a, :b]) === []) # ``` #
#
# Súgó # Javasoljuk a Tails.tails/1 használatát. Felhasználhatja a Repeated1.repeated/1 vagy a Repeated2.repeated/1függvényt is. #
defmodule Stammering1 do @spec stammering(xs :: [any()]) :: zss :: [[any()]] # zss az xs lista összes olyan nemüres, folytonos részlistájából # álló lista, amelyet vele azonos értékű részlista követ def stammering(xs) do for zs <- Tails.tails(xs) do # IO.inspect(zs) Repeated1.repeated(zs) end |> Enum.concat() end end (Stammering1.stammering([:a, :a, :a, 2, 3, 3, :a, :b, :b, :b, :b]) === [[:a], [:a], [3], [:b], [:b, :b], [:b], [:b]]) |> IO.puts() IO.puts(Stammering1.stammering([]) === []) IO.puts(Stammering1.stammering([:a]) === []) IO.puts(Stammering1.stammering([:a, :a]) === [[:a]]) IO.puts(Stammering1.stammering([:a, :b]) === []) # ### L7. Listában kulcs-érték párokban előforduló értékek listája # Egy listában többféle típusú és szerkezetű elem fordul elő, köztük {:v, v} párok is, ahol a párok első tagja a :v atom, második tagja az itt v-vel jelölt, tetszőleges érték. # Írjon olyan rekurzív függvényt, amelyik egy lista elemei közül az összes {:v, v} párban található v értéket visszaadja az eredménylistában. Írhat segédfüggvényt és akkumulátort nem használó, valamint akkumulátoros segédfüggvényt használó változatot. Feltétlenül írjon egyéb változatot is for-jelöléssel (meg fog lepődni!). #
# ```elixir # defmodule Ertekek do # @spec ertekek(xs :: [{:v::atom(), v::any()} | any()]) :: vs :: [any()] # # Az xs lista elemei közül a {:v, v} mintára illeszkedő # # párok 2. tagjából képzett lista vs # def ertekek(xs), do: for({:v, v} <- xs, do: v) # end # Ertekek.ertekek([:alma, {:s, 3}, {:v, 1}, 3, {:v, 2}]) === [1, 2] # ``` #
#
# Súgó # Ha a for-jelölés generátorában egy mintaillesztés sikertelen, akkor az adott érték nem kerül be az eredménylistába (nem teljesült a kiválasztási feltétel), és a kiértékelés a következő listaelemmel folytatódik. Ezért a for-ral külön szűrőfeltétel használata nélkül, nagyon egyszerűen megvalósítható az elvárt működés. #
defmodule Ertekek1 do @spec ertekek(xs :: [any()]) :: vs :: [any()] # Az xs lista elemei közül a {:v::atom(), v::any()} mintára illeszkedő # párok 2. tagjából képzett lista vs def ertekek([]), do: [] def ertekek([{:v, v} | xs]), do: [v | ertekek(xs)] def ertekek([_ | xs]), do: ertekek(xs) end Ertekek1.ertekek([:alma, {:s, 3}, {:v, 1}, 3, {:v, 2}]) === [1, 2] defmodule Ertekek2 do @spec ertekek(xs :: [any()]) :: vs :: [any()] # Az xs lista elemei közül a {:v::atom(), v::any()} mintára illeszkedő # párok 2. tagjából képzett lista vs def ertekek(xs), do: ertekek(xs, []) |> Enum.reverse() @spec ertekek(xs :: [any()], zs :: [any()]) :: vs :: [any()] def ertekek([], zs), do: zs def ertekek([{:v, v} | xs], zs), do: ertekek(xs, [v | zs]) def ertekek([_ | xs], zs), do: ertekek(xs, zs) end Ertekek2.ertekek([:alma, {:s, 3}, {:v, 1}, 3, {:v, 2}]) === [1, 2] defmodule Ertekek3 do @spec ertekek(xs :: [any()]) :: vs :: [any()] # Az xs lista elemei közül a {:v::atom(), v::any()} mintára illeszkedő # párok 2. tagjából képzett lista vs def ertekek(xs) do Enum.filter(xs, fn {:v, _} -> true _ -> false end) |> Enum.map(fn {:v, v} -> v end) end end Ertekek3.ertekek([:alma, {:s, 3}, {:v, 1}, 3, {:v, 2}]) === [1, 2] defmodule Ertekek4 do @spec ertekek(xs :: [any()]) :: vs :: [any()] # Az xs lista elemei közül a {:v::atom(), v::any()} mintára illeszkedő # párok 2. tagjából képzett lista vs def ertekek(xs), do: for({:v, v} <- xs, do: v) end Ertekek4.ertekek([:alma, {:s, 3}, {:v, 1}, 3, {:v, 2}]) === [1, 2] # ### T1. Természetes szám valódi osztói # Egy természetes szám valódi osztóinak nevezzük az 1-en és önmagán kívüli pozitív osztóit. # Írjon olyan kifejezést/függvényt a for jelölés felhasználásával, amelyik egy listában visszaadja a paraméterként átadott természetes szám valódi osztóit. # Írjon többféle megoldást, használjon magasabb rendű függvényeket, definiálhatja a függvényt a `def` kulcsszóval is. #
# ```elixir # # @spec proper_divisors(i :: integer()) :: ds :: [integer()] # # Az i természetes szám valódi osztóinak listája ds # proper_divisors = ... # (proper_divisors.(10) === [2, 5]) |> IO.inspect(charlists: :as_list) # (proper_divisors.(23) === []) |> IO.inspect(charlists: :as_list) # (proper_divisors.(48) === [2, 3, 4, 6, 8, 12, 16, 24]) \ # |> IO.inspect(charlists: :as_list) # (proper_divisors.(128) === [2, 4, 8, 16, 32, 64]) \ # |> IO.inspect(charlists: :as_list) # ``` #
#
# Súgó # Egy $k$ természetes szám valódi osztóit a legegyszerűbben úgy találhatja meg, hogy a $k$-t rendre elosztja a $2$ és $k/2$ közötti egészekkel, és ha az egészosztásnak nincs maradéka, akkor az adott szám osztója $k$-nak. #
proper_divisors = &for j <- 2..div(&1, 2), rem(&1, j) === 0, do: j (proper_divisors.(10) === [2, 5]) |> IO.inspect(charlists: :as_list) (proper_divisors.(23) === []) |> IO.inspect(charlists: :as_list) (proper_divisors.(48) === [2, 3, 4, 6, 8, 12, 16, 24]) |> IO.inspect(charlists: :as_list) (proper_divisors.(128) === [2, 4, 8, 16, 32, 64]) |> IO.inspect(charlists: :as_list) # ### T2. Összetett számok # Összetett számnak nevezzük azt a természetes számot, amelynek van valódi osztója. A legkisebb összetett szám a 4. # Írjon olyan kifejezést/függvényt a for jelölés felhasználásával, amelyik egy listában visszaadja a függvénynek paraméterként átadott természetes számnál nem nagyobb összes összetett számot, 4-től kezdve. A függvényben felhasználhatja a szám valódi osztóit előállító függvényt, amit az előbb írt meg. # Írjon többféle megoldást, használjon magasabb rendű függvényeket, definiálhatja a függvényt a `def` kulcsszóval is. #
# ```elixir # # @spec composite_numbers(i :: integer()) :: ns :: [integer()] # # Az i-nél nem nagyobb összetett számok listája ns # composite_numbers = # (composite_numbers.(11) === [4, 6, 8, 9, 10]) |> IO.inspect(charlists: :as_list) # (composite_numbers.(17) === [4, 6, 8, 9, 10, 12, 14, 15, 16]) \ # |> IO.inspect(charlists: :as_list) # ``` #
composite_numbers = &for j <- 4..&1, length(proper_divisors.(j)) > 0, do: j (composite_numbers.(11) === [4, 6, 8, 9, 10]) |> IO.inspect(charlists: :as_list) (composite_numbers.(17) === [4, 6, 8, 9, 10, 12, 14, 15, 16]) |> IO.inspect(charlists: :as_list) # #### Hatékonyabb megoldás # Írjon olyan megoldást az előző feladatra, amelyik nem állítja elő a valódi osztók listáját. # Az $n$ természetes szám összetett, ha osztható # 1. a $2$ és $\sqrt n$ közötti prímszámok bármelyikével; # 2. $2$-vel vagy a $3$ és $\sqrt n$ közötti páratlan egészek bármelyikével. # Az első módszer gyorsabb, de ha a prímszámok előállítása nem triviális, elég hatékony a második módszer is. További hatékonyságnövelő lehetőségekről olvashat pl. itt: pl. https://en.wikipedia.org/wiki/Primality_test. #
# ```elixir # composite_numbers_faster = &for i <- 4..&1, composite?.(i), do: i # (composite_numbers_faster.(11) === [4, 6, 8, 9, 10] ) \ # |> IO.inspect(charlists: :as_list) # (composite_numbers_faster.(21) === [4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21])\ # |> IO.inspect(charlists: :as_list) # ``` #
#
# # Súgó # # Javasoljuk, hogy a megoldást bontsa lépésekre: állítsa elő a szám összetett voltának vizsgálatához használandó osztókat; állapítsa meg, hogy a szám összetett szám-e; állítsa elő az összetett számok listáját a kért tartományban. Fontolja meg az alább felsorolt függvények használatát. # #
#
# ```elixir # # @spec probes(i :: integer()) :: ps :: [integer()] # # A 2-t, továbbá a 3 és a :math.sqrt(i) közé eső egészeket tartalmazó lista ps # probes = ... # # @spec composite?(i :: integer()) :: b :: boolean() # # b igaz, ha i összetett szám # composite? = ... # # @spec composite_numbers_faster(i :: integer()) :: ns :: [integer()] # # Az i-nél nem nagyobb összetett számok listája ns # composite_numbers_faster = ... # ``` #
probes = &[2 | Enum.to_list(3..floor(:math.sqrt(&1))//2)] composite? = &Enum.reduce(probes.(&1), false, fn x, y -> rem(&1, x) == 0 or y end) composite_numbers_faster = &for i <- 4..&1, composite?.(i), do: i (composite_numbers_faster.(11) === [4, 6, 8, 9, 10]) |> IO.inspect(charlists: :as_list) (composite_numbers_faster.(21) === [4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21]) |> IO.inspect(charlists: :as_list) # A megoldást, ha a javaslatunkat megfogadta, három kis – esetleg több – lépésre bontotta, mindegyikre egy-egy névtelen (de változóhoz kötött) függvényt írt, így ezeket könnyebb volt megérteni, és a helyességüket könnyebb volt belátni. Mivel a funkcionális nyelvekben a függvényhívás nagyon hatékonyan van megoldva, ez a jó és követendő gyakorlat: minden kicsit is összetett függvényt több egyszerűbb függvényből rakjunk össze, és ahol csak lehet, használjuk a hatékonyan megvalósított és sokat tesztelt könyvtári függvényeket.