# Run as: iex --dot-iex dp24a-fp1gy-megoldas.exs # Title: DP24a - 1. FP-gyakorlat, 2024-09-03 # ── Egyszerű feladatok listák feldolgozására ── # ### Lista feje 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) # ### Lista farka 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") # ### Lista n-edik eleme 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) # ### Lista hossza 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! 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], cnt), do: len(xs, cnt + 1) def len([], cnt), do: cnt end IO.puts(Length2.len([]) == 0) IO.puts(Length2.len(Range.to_list(1..5)) == 5) IO.puts(Length2.len(~c"kőszerű") == 7) # ### Lista utolsó eleme 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?") == ??) #HP IO.puts(Last.last([])) # Erre a hívásra nem illeszkedik klóz! # Most írja meg a függvényt Elixir-stílusú hibakezeléssel! 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! 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) # ### Lista $k$-adik elemétől induló, $n$ hosszú részlistája 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([], _k, 0), do: [] def slice([], _k, _n), do: nil 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) 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) Slice.slice(Range.to_list(1..5), 4, 1) # ### Tagsági vizsgálat defmodule IsMemb do @spec is_memb(xs :: [any()], e :: any()) :: b :: true | false # b == true, ha e benne van xs-ben, egyénként false def is_memb([e | _xs], e), do: true def is_memb([_ | xs], e), do: is_memb(xs, e) def is_memb([], _e), do: false end IsMemb.is_memb(~c"A szó elszáll", ?ó) |> IO.inspect IsMemb.is_memb([~c"A szó", ~c"elszáll", ~c"az írás", ~c"megmarad."], ~c"elszáll") |> IO.inspect IsMemb.is_memb([1.2, ?v, "str", false], false) |> IO.inspect IsMemb.is_memb([1.2, ?v, "str", false], "str") |> IO.inspect IsMemb.is_memb([1.2, ?v, "str", false], ~c"str") |> IO.inspect IsMemb.is_memb([], [])|> IO.inspect # ── Bal-, jobb- és törzsrekurzió ── defmodule Fac do @spec fac(n :: integer()) :: f :: integer() # f = n! def fac(0), do: 1 def fac(n), do: n * fac(n - 1) end Fac.fac(25) |> IO.inspect Fac.fac(0) |> IO.inspect #HP Ezt a modult nem lehet lefordítani, kifejezés nem adható át paraméterként a második klózban. #HP defmodule FacBad do #HP @spec fac(n :: integer()) :: f :: integer() #HP # f = n! #HP def fac(0), do: 1 #HP def fac(n >= 0), do: n * fac(n - 1) #HP end # Az ehhez hasonló esetek elég gyakoriak, ezért a mintát ún. _őrrel_ egészíthetjük ki. defmodule FacGuarded do @spec fac(n :: integer()) :: f :: integer() # f = n! def fac(0), do: 1 def fac(n) when n >= 0, do: n * fac(n - 1) end FacGuarded.fac(25) |> IO.inspect FacGuarded.fac(0) |> IO.inspect #HP FacGuarded.fac(-1) |> IO.inspect # Erre a hívásra nem illeszkedik klóz! # A hibakezelést vagy egy hibatűrő verzió megírását meghagyjuk gyakorló feladatnak. # ### Kiírás rekurzív hívás előtt és után defmodule UptoBy31 do @spec upto_by_3(n :: integer()) :: :ok def upto_by_3(n), do: upto_by_3(n, 3) @spec upto_by_3(n :: integer(), i :: integer()) :: :ok def upto_by_3(n, i) when i <= n do IO.puts(i) upto_by_3(n, i + 3) end def upto_by_3(_, _), do: :ok end UptoBy31.upto_by_3(20) # #### Kiírás a rekurzív hívás után defmodule UptoBy32 do @spec upto_by_3(n :: integer()) :: :ok def upto_by_3(n), do: upto_by_3(n, 3) @spec upto_by_3(n :: integer(), i :: integer()) :: :ok def upto_by_3(n, i) when i <= n do upto_by_3(n, i + 3) IO.puts(i) end def upto_by_3(_, _), do: :ok end UptoBy32.upto_by_3(20) defmodule UptoBy33 do @spec upto_by_3(n :: integer()) :: :ok def upto_by_3(n), do: downto_by_3(div(n, 3)) @spec downto_by_3(i :: integer()) :: :ok def downto_by_3(0), do: :ok def downto_by_3(i) do downto_by_3(i - 1) IO.puts(i * 3) end end UptoBy33.upto_by_3(20)