# Run as: iex --dot-iex path/to/notebook.exs
# Title: DP25a - 1. FP-gyakorlat, 2025-09-09
# ── Jelölések, konvenciók ──
# 
# A gyakorlatok anyaga szakaszokra van felosztva, minden szakaszban a bevezetés után néhány feladatot definiálunk, néha megoldott feladatokat is bemutatunk.
#
# ```
# Halványzöld peremű, fekete hátterű cellában (a továbbiakban: specifikációs cella) van
# a szükséges „keretezéssel”, azaz a modul- és függvénydefinícióval együtt a megírandó
# függvény típusspecifikációja, valamint néhány teszthívás is. Ebbe a cellába nem lehet
# beleírni (csak szerkesztő módban), de a tartalmát ki lehet jelölni, lehet másolni.
# ```
#
#
# Rózsaszín hátterű cellába írjuk az esetleges korlátozásokat: ne használja ezt, ne csinálja azt stb.
#
# Halványzöld hátterű cellában jelennek meg a magyarázataink, illetve a javaslataink egyes feladatok megoldására. Az utóbbiak gyakran el vannak rejve: a Súgó feliratra kattintva jelennek meg.
#
# Az egymást kölcsönösen kizáró minták használata...
#
#
# Súgó
# Ezt és ezt javasoljuk a függvény megírásához.
#
# A feladatot megoldó függvényt, kifejezést egy Elixir-cellába írja be: a felugó menüben a + Elixir feliratra kattintva hozzon létre egy új cellát, másolja be a specifikációs cella tartalmát, majd írja meg és értékelje ki a specifikált függvényt vagy a kért kifejezést.
# ── Első lépések ──
# 
# Válassza ki, hogy a _Livebookot_ (esetleg külön az _Elixirt_ is) hogyan kívánja telepíteni a saját gépére: Docker konténerként, a Mac, ill. a Windows telepítőkkel, Debian/Ubuntu esetén az _apt_, a _mise_ vagy az _asdf_ csomagkezelővel. Az első előadás prezentációs anyagában néhány dián összefoglaltuk a lehetőséget.
# Kezdje a munkát a kiválasztott eszközök telepítésével, az ismerkedéssel. Hozzon létre Elixir-cellát a _Livebookban_, inditsa el egy terminálablakban az _iex_-et, írjon be néhány egyszerű kifejezést, hívjon meg közismert függvényeket a `Kernel` Elixir- vagy a `:math` Erlang-könyvtárból, figyelje meg a _szövegkiegészítés_ (completion) és a _Livebookban_ a _felugró súgó_ működését. Ha elakad, kérjen segítséget a gyakorlatvezetőktől vagy társaitól.
# Ez a fájl az első tantermi gyakorlat anyagát tartalmazza. Ha a feladatsort nem sikerül befejeznie a gyakorlaton, oldja meg otthon. Lehetőség van távkonzultációra: üzenjen nekünk a Teams csoportban!
# ── Rekurzió ──
# 
#
# A deklaratív programozás alappillére a rekurzió. A rekurzió kétféle lehet: lineáris és elágazó (angolul linear recursion és tree recursion). Lineárisan rekurzív adatszerkezet a lista (láncolt lista, nem egydimenziós tömb!), elágazóan rekurzív a (bináris vagy több ágú) fa, ezek feldolgozására értelemszerűen rekurzív algoritmusokat írunk. De rekurzív algoritmusokat kell használnunk iterációk megvalósítására is ciklusok helyett, mivel az utóbbiak a deklaratív nyelvekben ismeretlen konstrukciók.
#
# ── Egyszerű feladatok listák feldolgozására ──
#
# Rekurzív adatszerkezetek feldolgozásának természetes módja a rekurzív algoritmus. Lineáris adatszerkezetek pl. listák feldolgozására – imperatív nyelveken – még csak lehet ciklust írni, de elágazóan rekurzív adatszerkezeteket, pl. fákat ciklusokkal bejárni már nagy kihívás.
# Egy rekurzív adatszerkezet feldolgozására legalább két, esetleg több klózt írunk. Közöttük vannak olyanok, amelyekre az adatszerkezet jellegzetessége miatt csak egyszer vagy csak nagyon ritkán, másokra gyakrabban kerül sor. Ilyen például az üres és a nemüres lista esete: az üres listát feldolgozó klóz kiértékelésére csak egyszer kerül sor, a nemüres listát feldolgozó klózt többször, a lista hosszától függően esetleg nagyon sokszor meghívjuk.
# Az algoritmus hatékonyságát javítja, ha
# * egy függvény klózai _kölcsönösen kizárják_ egymást, és
# * közülük a gyakrabban hívott(ak) megelőzi(k) a ritkábban hívott(ak)at.
# `def fun([x|xs])...`\
# `def fun([])...`
#
#
# Gyakran előfordul, hogy bizonyos listákon bizonyos műveleteket nem lehet elvégezni. Pl. egy üres listának egyetlen eleme sincs, bármelyik elemét is kérjük, nincs mit eredményül adni.
#
# Ilyenkor dönthetünk úgy, hogy az adott műveletet üres listára nem értelmezzük, és rábízzuk a rendszerre a hiba jelzését. Ha úgy döntünk, hogy jelezzük a helyes eredmény mellett a hibát is, akkor ezt hagyományosan kétféle stílusban tehetjük meg:
# * Erlang-stílusban a visszatérési érték típusa `{:ok, any()} | :error`, azaz siker esetén a visszatérési érték egy `{:ok, value}` pár, ahol egy `any()` típusú `value` a visszaadott érték, meghiúsulás esetén pedig a visszatérési érték az `:error` atom;
# * Elixir-stílusban a visszatérési érték típusa `any() | nil`, azaz siker esetén ugyancsak egy `any()` típusú `value` a visszatérési érték, meghiúsulás esetén pedig a `nil` atom.
#
#
#
# Az alábbi feladatok megoldására __ne__ használja az azonos vagy hasonló feladatokat megoldó könyvtári függvényeket a `Kernel`, `List`, `Enum` és más modulokból, pl. `hd`, `tl`, `first`, `last`, `at`, `length`, `split`, `slice`!
# Azt kérjük, hogy most gyakorlásképpen _saját_ – ahol kell, rekurzív – függvénydefiníciókat írjon.
#
# ### 1. Lista feje
# Írjon függvényt egy lista fejének (első elemének) visszaadására! Ha a lista üres, Elixir-stílusban jelezze, azaz a `nil` atomot adja eredményül.
#
# Súgó
# Az első klóz a legalább egy elemű listára, a második pedig az üres listára illeszkedjen.
#
#
# ```elixir
# defmodule Head do
# @spec hd(xs :: [any()]) :: r :: (x :: any()) | nil
# # Ha xs nem üres, r == x, az xs lista feje, egyébként r == nil
# def hd(xs), do:
# ...
# end
# IO.puts(Head.hd([]) == nil)
# IO.puts(Head.hd(Range.to_list(1..5)) == 1)
# IO.puts(Head.hd(~c"almárium") == ?a)
# ```
#
defmodule Head do
@spec hd(xs :: [any()]) :: r :: (x :: any()) | nil
# Ha xs nem üres, r == x, az xs lista feje, egyébként r == nil
def hd([x|_]), do: x
def hd([]), do: nil
end
IO.puts(Head.hd([]) == nil)
IO.puts(Head.hd(Range.to_list(1..5)) == 1)
IO.puts(Head.hd(~c"almárium") == ?a)
# ### 2. Lista farka
# Írjon függvényt egy lista farkának (az első elemét követő részlistájának) a visszaadására! Ha a lista üres, Elixir-stílusban jelezze, azaz a `nil` atomot adja eredményül.
#
# Súgó
# Az első klóz a legalább egy elemű listára, a második pedig az üres listára illeszkedjen.
#
#
# ```elixir
# defmodule Tail do
# @spec tl(xs :: [any()]) :: r :: (ts :: any()) | nil
# # Ha xs nem üres, r == ts, az xs lista farka, egyébként r == nil
# @spec tl(xs: [any()]) :: ts :: [any()] | nil
# # Az xs lista farka ts
# def tl(xs), do:
# ...
# end
# IO.puts(Tail.tl([]) == nil)
# IO.puts(Tail.tl(Range.to_list(1..5)) == [2,3,4,5])
# IO.puts(Tail.tl(~c"almárium") == ~c"lmárium")
# ```
#
defmodule Tail do
@spec tl(xs :: [any()]) :: r :: (ts :: any()) | nil
# Ha xs nem üres, r == ts, az xs lista farka, egyébként r == nil
def tl([_|xs]), do: xs
def tl([]), do: nil
end
IO.puts(Tail.tl([]) == nil)
IO.puts(Tail.tl(Range.to_list(1..5)) == [2,3,4,5])
IO.puts(Tail.tl(~c"almárium") == ~c"lmárium")
# ### 3. Lista n-edik eleme
# Írjon rekurzív függvényt egy lista n-edik elemének a visszaadására! (A lista indexelése 0-tól indul.) Ha a lista n-nél rövidebb, Elixir-stílusban jelezze, azaz a `nil` atomot adja eredményül.
#
# Súgó
# Itt három klózt kell írnia: egyet az üres listára, egyet a megtalált elem visszaadására, egyet pedig arra, hogy rekurzív hívással folytassa a keresést.
#
#
# ```elixir
# defmodule Nth do
# @spec nth(xs :: [any()], n :: integer()) :: r :: (x :: any()) | nil
# # Ha xs elég hosszú, r == x, az xs n-edik eleme; egyébként r == nil (indexelés 0-tól)
# def nth(xs, n), do:
# ...
# end
# IO.puts(Nth.nth([], 5) == nil)
# IO.puts(Nth.nth(Range.to_list(1..5), 4) == 5)
# IO.puts(Nth.nth(Range.to_list(1..5), 5) == nil)
# IO.puts(Nth.nth(~c"almárium", 3) == ?á)
# IO.puts(Nth.nth(~c"almárium", -3) == nil)
# ```
#
defmodule Nth do
@spec nth(xs :: [any()], n :: integer()) :: r :: (x :: any()) | nil
# Ha xs elég hosszú, r == x, az xs n-edik eleme; egyébként r == nil (indexelés 0-tól)
def nth([x|_], 0), do: x
def nth([_|xs], n), do: nth(xs, n-1)
def nth([], _), do: nil
end
IO.puts(Nth.nth([], 5) == nil)
IO.puts(Nth.nth(Range.to_list(1..5), 4) == 5)
IO.puts(Nth.nth(Range.to_list(1..5), 5) == nil)
IO.puts(Nth.nth(~c"almárium", 3) == ?á)
IO.puts(Nth.nth(~c"almárium", -3) == nil)
# A `benchee` Elixir-modul és a `mix` segítségével hasonlítsa össze a futási időket:
# * egy hosszú lista első, középső és utolsó elemének elérése esetén,
# * olyan klózsorrendek esetén, amikor az első, illetve az utolsó klóz illeszkedik az üres listára.
# ### 4. Lista hossza
# Írjon rekurzív függvényt egy lista hosszának meghatározására! Ne használjon segédfüggvényt!
#
# Súgó
# Az első klóz a legalább egy elemű listákra, a második pedig az üres listára illeszkedjen.
#
#
# ```elixir
# defmodule Length do
# @spec len(xs :: [any()]) :: n :: integer()
# # Az xs lista hossza n
# def len(xs), do:
# ...
# end
# IO.puts(Length.len([]) == 0)
# IO.puts(Length.len(Range.to_list(1..5)) == 5)
# IO.puts(Length.len(~c"kőszerű") == 7)
# ```
#
defmodule Length do
@spec len(xs :: [any()]) :: n :: integer()
# Az xs lista hossza n
def len([_ | xs]), do: len(xs) + 1
def len([]), do: 0
end
IO.puts(Length.len([]) == 0)
IO.puts(Length.len(Range.to_list(1..5)) == 5)
IO.puts(Length.len(~c"kőszerű") == 7)
# A lista hosszának megállapítására most akkumulátort és segédfüggvényt használó _jobbrekurzív_ függvényt írjon!
#
# Súgó
# Az akkumulátorban gyűjtse a listaelemek számát: amikor leszedi a soron következő elemet a lista elejéről, akkor a rekurzív hívásban az akkumulátor korábbi értékéhez adjon 1-et.
#
#
# ```elixir
# defmodule Length2 do
# @spec len(xs :: [any()]) :: n :: integer()
# # Az xs lista hossza n
# def len(xs), do: ...
# @spec len(xs :: [any()], count :: integer()) :: n :: integer()
# # Az xs lista hossza és count összege n
# def len(xs, count), do:
# ...
# end
# IO.puts(Length2.len([]) == 0)
# IO.puts(Length2.len(Range.to_list(1..5)) == 5)
# IO.puts(Length2.len(~c"kőszerű") == 7)
# ```
#
defmodule Length2 do
@spec len(xs :: [any()]) :: n :: integer()
# Az xs lista hossza n
def len(xs), do: len(xs, 0)
@spec len(xs :: [any()], count :: integer()) :: n :: integer()
# Az xs lista hossza és count összege n
def len([_ | xs], count), do: len(xs, count + 1)
def len([], count), do: count
end
IO.puts(Length2.len([]) == 0)
IO.puts(Length2.len(Range.to_list(1..5)) == 5)
IO.puts(Length2.len(~c"kőszerű") == 7)
# ### 5. Lista utolsó eleme
# Írjon rekurzív függvényt egy lista utolsó elemének visszaadására! Ne használjon segédfüggvényt!
# ##### Esetek megkülönböztetése kölcsönös kizárással
#
# A függvénynek, amennyiben az üres listát nem kell kezelnie, két esetet kell megkülönböztetnie: azt, amikor a listának pontosan egy eleme van, és azt, amikor a listának legalább egy eleme van. A korábban látott sémát csak kicsit kell módosítanunk: a második klóznak nem az üres listára, hanem az egyelemű listára kell illeszkednie.
# `def fun([x|xs])...`\
# `def fun([x])...`
# Csakhogy ezzel van egy kis gond: az első klóz ___minden___ olyan listára illeszkedik, amelyiknek van feje, a farka pedig tetszőleges elemszámú, azaz üres is lehet. Emiatt az első klóz az egyelemű listára is illeszkedik, azaz a második klózra soha nem kerül sor – a minták nem egymást kölcsönösen kizáróak. (Erre figyelmeztet is az Elixir fordító). A két klóz sorrendjének megfordításával a mintaillesztés a két esetet meg tudja különböztetni, ám ennek hatékonyságromlás az ára.
# `def fun([x])...`\
# `def fun([x|xs])...`
# Lehet azonban olyan mintát is írni, amelyik legalább két elemű listákra illeszkedik, és ezáltal kölcsönös kizárásban van a pontosan egy egyelemű listára illeszkedő mintával:
# `def fun([x1,x2|xs])...`\
# `def fun([x])...`
# Az első klóz törzsében a lista fejére az `x1` változóval, a lista farkára az `[x2|xs]` kifejezéssel hivatkozhatunk. Az utóbbi hivatkozást egyszerűbbé (és olcsóbbá!) tehetjük az ún. _réteges mintával_ (angolul: _layered pattern):_
# `def fun([x1 | xxs = [x2|xs])...`\
# `def fun([x])...`
# A lista farkára az `xxs` változóval lehet hivatkozni. Ha az `x2` és `xs` változókat _nem használjuk_ a klóz törzsében, akkor erre az Elixir fordító figyelmeztetni fog. A figyelmeztetést úgy kerülhetjük el, hogy a változó nevét aláhúzásjellel (`_`) kezdjük.
# `def fun([x1 | xxs = [_x2|_xs])...`\
# `def fun([x])...`
# Az aláhúzásjellel kezdődő nevek helyett elég aláhúzásjelet írni:
# `def fun([x1 | xxs = [_|_])...`\
# `def fun([x])...`,
# a beszédes nevek azonban segítik a megértést, utalnak a változó szerepére.
# Ha a lista második elemére vagy a harmadik elemtől kezdődő farkára akarunk hivatkozni a klóz törzsében, akkor inkább ne aláhúzásjellel kezdődő változóneveket használjunk.
#
# Először is írjon egy olyan függvényt, amelyik visszadja egy lista utolsó elemét, de a hibajelzést rábízza a rendszerre.
#
# ```elixir
# defmodule Last do
# @spec last(xs :: [any()]) :: x :: any()
# # Ha xs nem üres, az utolsó eleme x
# def last(xs), do:
# ...
# end
# IO.puts(Last.last(~c"Élvezed?") == ??)
# IO.puts(Last.last([]))
# ```
#
defmodule Last do
@spec last(xs :: [any()]) :: x :: any()
# Ha xs nem üres, az utolsó eleme x
def last([_ | [_ | _] = xs]), do: last(xs)
def last([x]), do: x
end
IO.puts(Last.last(~c"Élvezed?") == ??)
IO.puts(Last.last([]))
# Most írja meg a függvényt Elixir-stílusú hibakezeléssel!
#
# ```elixir
# defmodule LastEx do
# @spec last(xs :: [any()]) :: r :: (x :: any()) | nil
# # Ha xs nem üres, r == x, ahol az xs utolsó eleme x, egyébként r == nil
# def last(xs), do:
# ...
# end
# IO.puts(LastEx.last(~c"Élvezed?") == ??)
# IO.puts(LastEx.last([]) == nil)
# ```
#
defmodule LastEx do
@spec last(xs :: [any()]) :: r :: (x :: any()) | nil
# Ha xs nem üres, r == x, ahol az xs utolsó eleme x, egyébként r == nil
def last([_ | [_ | _] = xs]), do: last(xs)
def last([x]), do: x
def last([]), do: nil
end
IO.puts(LastEx.last(~c"Élvezed?") == ??)
IO.puts(LastEx.last([]) == nil)
# Most írja meg újra a függvényt Erlang-stílusú hibakezeléssel!
#
# ```elixir
# defmodule LastEr do
# @spec last(xs :: [any()]) :: r :: {:ok, x :: any()} | :error
# # Ha xs nem üres, r == {:ok, x}, ahol az xs utolsó eleme x, egyébként r == :error
# def last(xs), do:
# ...
# end
# IO.puts(LastEr.last(~c"Élvezed?") == {:ok, ??})
# IO.puts(LastEr.last([]) == :error)
# ```
#
defmodule LastEr do
@spec last(xs :: [any()]) :: r :: {:ok, x :: any()} | :error
# Ha xs nem üres, r == {:ok, x}, ahol az xs utolsó eleme x, egyébként r == :error
def last([_ | xs = [_ | _]]), do: last(xs)
def last([x]), do: {:ok, x}
def last([]), do: :error
end
IO.puts(LastEr.last(~c"Élvezed?") == {:ok, ??})
IO.puts(LastEr.last([]) == :error)
# ### 6. Lista $k$-adik elemétől induló, $n$ hosszú részlistája
# Írjon rekurzív függvényt egy lista olyan $n$ hosszú részlistájának a visszaadására, amelyik a $k$-adik elemtől kezdődik (a lista indexelése 0-tól indul)! Ha nincs ilyen hosszú részlistája, Elixir-típusú hibajelzéssel térjen vissza. Ne használjon segédfüggvényt! A klózok sorrendjének megválasztásával törekedjék hatékony megoldásra.
# Gondolja át, hányféle esetet kell megkülönböztetnie!
#
# Súgó
#
# 1. Kiszedtük a lista $k$-adik elemével kezdődő, $n$ hosszú részlistát.
# 1. Elhagytuk a lista első $k-1$ elemét, kigyűjthetjük a következő $n$ darab elemet.
# 1. Elhagyjuk a lista első $k-1$ elemét.
# 1. Elfogytak az elemek a listából, mielőtt kiszedtük volna az $n$ hosszú részlistát.
#
#
# ```elixir
# defmodule Slice do
# @spec slice(xs :: [any()], k :: integer(), n :: integer()) :: r :: (ss :: [any()]) | nil
# # Ha xs elég hosszú, r == ss, az xs k-tól induló, n hosszú részlistája; egyébként r == nil
# # Indexelés 0-tól
# def slice(xs, k, n), do:
# ...
# end
# IO.puts(Slice.slice([], 0, 5) == nil)
# IO.puts(Slice.slice(Range.to_list(1..5), 1, 3) == [2,3,4])
# IO.puts(Slice.slice(Range.to_list(1..5), 4, 1) == [5])
# IO.puts(Slice.slice(~c"almárium", 3, 3) == ~c"ári")
# IO.puts(Slice.slice(~c"almárium", -3, 3) == nil)
# ```
#
defmodule Slice do
@spec slice(xs :: [any()], k :: integer(), n :: integer()) :: r :: (ss :: [any()]) | nil
# Ha xs elég hosszú, r == ss, az xs k-tól induló, n hosszú részlistája; egyébként r == nil
# Indexelés 0-tól
def slice(_xs, _k, 0), do: []
def slice([x|xs], 0, n), do: [x | slice(xs, 0, n-1)]
def slice([_x|xs], k, n), do: slice(xs, k-1, n)
def slice([], _k, _n), do: nil
end
IO.puts(Slice.slice([], 0, 5) == nil)
IO.puts(Slice.slice(Range.to_list(1..5), 1, 3) == [2,3,4])
IO.puts(Slice.slice(Range.to_list(1..5), 4, 1) == [5])
IO.puts(Slice.slice(~c"almárium", 3, 3) == ~c"ári")
IO.puts(Slice.slice(~c"almárium", -3, 3) == nil)
# A `benchee` Elixir-modul és a `mix` segítségével mérje meg a futási időket különféle paraméterezések mellett, továbbá profilírozással nézze meg, hogy melyik klóz, illetve hívott függvény használja el a legtöbb futási időt. Hasonlítsa össze a saját `Slice.slice/3` függvényének futási idejét az `Enum.slice/3` függvény futási idejével különféle szélsőséges paraméterezések mellett.
# ### 7. Tagsági vizsgálat
# Írjon rekurzív függvényt annak eldöntésére, hogy egy érték benne van-e egy listában. Ne használjon segédfüggényt, ügyeljen a hatékonyságra.
#
# ```elixir
# defmodule Member do
# @spec member?(xs :: [any()], e :: any()) :: b :: boolean()
# # b == true, ha e benne van xs-ben, egyénként false
# def member?(xs, e), do:
# ...
# end
# (Member.member?(~c"A szó elszáll", ?ó) == true) |> IO.inspect()
# (Member.member?([~c"A szó", ~c"elszáll", ~c"az írás", ~c"megmarad."], ~c"elszáll")
# == true) |> IO.inspect()
# (Member.member?([1.2, ?v, "str", false], false) == true) |> IO.inspect()
# (Member.member?([1.2, ?v, "str", false], "str") == true) |> IO.inspect()
# (Member.member?([1.2, ?v, "str", false], ~c"str") == false) |> IO.inspect()
# (Member.member?([], []) == false) |> IO.inspect()
# ```
#
defmodule Member do
@spec member?(xs :: [any()], e :: any()) :: b :: boolean()
# b == true, ha e benne van xs-ben, egyénként false
def member?([e | _xs], e), do: true
def member?([_ | xs], e), do: member?(xs, e)
def member?([], _e), do: false
end
(Member.member?(~c"A szó elszáll", ?ó) == true) |> IO.inspect()
(Member.member?([~c"A szó", ~c"elszáll", ~c"az írás", ~c"megmarad."], ~c"elszáll")
== true) |> IO.inspect()
(Member.member?([1.2, ?v, "str", false], false) == true) |> IO.inspect()
(Member.member?([1.2, ?v, "str", false], "str") == true) |> IO.inspect()
(Member.member?([1.2, ?v, "str", false], ~c"str") == false) |> IO.inspect()
(Member.member?([], []) == false) |> IO.inspect()
# ### 8. Prímvizsgálat
# Írjon rekurzív programot annak eldöntésére, hogy egy egész szám prím-e. Feltételezheti, hogy a függvényt egészszám-paraméterrel hívjuk. (Az első előadás egyik Prolog-példája volt ez a feladat.) Segédfüggvényt használhat. Ügyeljen a hatékonyságra.
#
# ```elixir
# defmodule Prime do
# @spec prime?(x :: integer()) :: b :: boolean()
# # b == true, ha x prím
# def prime?(x), do:
# ...
# end
# (Prime.prime?(17) == true) |> IO.inspect()
# (Prime.prime?(18) == false) |> IO.inspect()
# (Prime.prime?(197_628) == false) |> IO.inspect()
# (Prime.prime?(1_000_003) == true) |> IO.inspect()
# (Prime.prime?(1_213_457) == false) |> IO.inspect()
# (Prime.prime?(179_424_691) == true) |> IO.inspect()
# ```
#
defmodule Prime do
@spec prime?(x :: integer()) :: b :: boolean()
# b == true, ha x prím
def prime?(x), do: is_prime(x, [2 | Enum.to_list(3..div(x, 2) // 2)])
defp is_prime(x, [n|ns]), do: (rem(x, n) !== 0) && is_prime(x, ns)
defp is_prime(_, []), do: true
end
(Prime.prime?(17) == true) |> IO.inspect()
(Prime.prime?(18) == false) |> IO.inspect()
(Prime.prime?(197_628) == false) |> IO.inspect()
(Prime.prime?(1_000_003) == true) |> IO.inspect()
(Prime.prime?(1_213_457) == false) |> IO.inspect()
(Prime.prime?(179_424_691) == true) |> IO.inspect()
# ── További gyakorló feladatok ──
#
# ```elixir
# defmodule Fp1Gy do
# @spec revapp(xs :: [integer()], ys :: [integer()]) :: zs :: [integer()]
# # zs == xs fordítottja ys elé fűzve
# def revapp(xs, ys) do
# ...
# end
# @spec rev(xs :: [integer()]) :: zs :: [integer()]
# # zs == xs fordítottja
# def rev(xs) do
# ...
# end
# @spec diff(xs :: [integer()], ys :: [integer()]) :: zs :: [integer()]
# # zs == xs és ys különbsége, azaz xs azon elemei, melyek nincsenek benne ys-ben
# # a listában az azonos értékű elemeket külön elemeknek tekintjük,
# # azaz az ilyen lista zsák (bag), nem halmaz (set)
# def diff(xs, ys) do
# ...
# end
# end
# ```
#