4. domača naloga#
Slovarji#
Na predavanjih in vajah smo si ogledali iskalna drevesa in implementacijo
AVL-dreves za predstavitev množic. V tej nalogi morate s pomočjo AVL-dreves
implementirati slovar, ki preslika ključe tipa 'k v vrednosti tipa 'v.
Stroga ureditev#
Za predstavitev slovarja potrebujemo strogo ureditev na tipu ključev.
Najprej definiramo tip ureditev, ki predstavlja možne izide
primerjave dveh elementov, nato pa še modul UREJEN_TIP, s katerim
lahko primerjamo abstraktne elemente.
type ureditev = Less | Equal | Greater
module type UREJEN_TIP = sig
type t
val primerjaj : t -> t -> ureditev
end
module INT_UREJEN_TIP : UREJEN_TIP with type t = int = struct
type t = int
let primerjaj x y = if x < y then Less else if x > y then Greater else Equal
end
type ureditev = Less | Equal | Greater
module type UREJEN_TIP = sig type t val primerjaj : t -> t -> ureditev end
module INT_UREJEN_TIP :
sig type t = int val primerjaj : t -> t -> ureditev end
Sestavite modul STRING_UREJEN_TIP, ki implementira UREJEN_TIP za tip
string.
module STRING_UREJEN_TIP : UREJEN_TIP with type t = string = struct
type t = string
let primerjaj x y = assert false
end;;
STRING_UREJEN_TIP.primerjaj "abc" "abd"
(* - : ureditev = Less *)
module STRING_UREJEN_TIP :
sig type t = string val primerjaj : t -> t -> ureditev end
- : ureditev = Less
Za poljuben tip lahko definiramo razširitev z najmanjšim in največjim
elementom. Sestavite parametriziran modul RAZSIRJEN_UREJEN_TIP, ki
sprejme modul, ki implementira signaturo UREJEN_TIP, in vrne modul, ki
implementira signaturo UREJEN_TIP za razširjeni tip.
type 'a razsiritev = MinInf | PlusInf | Value of 'a
module RAZSIRJEN_UREJEN_TIP (U : UREJEN_TIP) :
UREJEN_TIP with type t = U.t razsiritev = struct
type t = U.t razsiritev
let primerjaj x y = assert false
end
module LIFTED_INT_UREJEN_TIP = RAZSIRJEN_UREJEN_TIP (INT_UREJEN_TIP);;
LIFTED_INT_UREJEN_TIP.primerjaj MinInf (Value 3)
(* - : ureditev = Less *)
type 'a razsiritev = MinInf | PlusInf | Value of 'a
module RAZSIRJEN_UREJEN_TIP :
functor (U : UREJEN_TIP) ->
sig type t = U.t razsiritev val primerjaj : t -> t -> ureditev end
module LIFTED_INT_UREJEN_TIP :
sig
type t = INT_UREJEN_TIP.t razsiritev
val primerjaj : t -> t -> ureditev
end
- : ureditev = Less
AVLSlovar#
Sestavite parametriziran modul MAKE_SLOVAR, ki sprejme modul, ki
implementira UREJEN_TIP, in vrne modul s signaturo SLOVAR. Vaš slovar
naj bo implementiran z AVL-drevesi, tako da je vstavljanje in iskanje v
slovarju v času O(log n).
module type SLOVAR = sig
type kljuc
type 'a t
val prazen : 'a t
(** Vrne prazen slovar. *)
val dodaj : kljuc -> 'a -> 'a t -> 'a t
(** Doda nov par `kljuc`, `vrednost` v slovar. Če ključ v slovarju že obstaja,
se njegova vrednost posodobi. *)
val popravi : kljuc -> ('a option -> 'a option) -> 'a t -> 'a t
(** Popravi vrednost pod ključem `kljuc` s funkcijo `f`. Če ključ v slovarju
ne obstaja, se pokliče `f None`, sicer `f (Some vrednost)`. Če je rezultat
klica `f` enak `None`, se par odstrani iz slovarja, če je rezultat klica
`Some v`, se pod ključ `kljuc` zapiše vrednost `v`.*)
val odstrani : kljuc -> 'a t -> 'a t
(** Odstrani par s ključem `kljuc` iz slovarja. Če ključa v slovarju ni, naj
funkcija vrne prvotni slovar in ne sproži napake. *)
val velikost : 'a t -> int
(** Vrne število elementov v slovarju. *)
val kljuci : 'a t -> kljuc list
(** Našteje ključe v slovarju v enakem vrstnem redu kot to določa urejenost. *)
val vrednosti : 'a t -> 'a list
(** Našteje vrednosti v slovarju v enakem vrstnem redu kot to določa urejenost
pripadajočih ključev. *)
val najmanjsi_opt : 'a t -> (kljuc * 'a) option
(** Vrne najmanjši ključ v slovarju ali `None`, če je slovar prazen. *)
val najvecji_opt : 'a t -> (kljuc * 'a) option
(** Vrne največji ključ v slovarju ali `None`, če je slovar prazen. *)
val poisci_opt : kljuc -> 'a t -> 'a option
(** Poišče vrednost pod ključem `kljuc`. Če ključ v slovarju ne obstaja,
vrne `None`. *)
val iter : (kljuc -> 'a -> unit) -> 'a t -> unit
(** Izvede funkcijo za vsak par ključ, vrednost v slovarju v enakem vrstnem
redu kot ga določa urejenost. *)
val zlozi : (kljuc -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
(** Zloži slovar z dano funkcijo in začetno vrednostjo. Elementi se obdelajo
v enakem vrstnem redu kot ga določa urejenost.
Specifično za
`zlozi f slovar acc = f k_n v_n (... (f k_2 v_2 (f k_1 v_1 acc))...)`
, kjer so `(k_1, v_1), ..., (k_n, v_n)` pari ključ, vrednost v slovarju
urejeni po ključih.
*)
val preslikaj : ('a -> 'b) -> 'a t -> 'b t
(** Preslika vrednosti v slovarju z dano funkcijo. *)
val preslikaji : (kljuc -> 'a -> 'b) -> 'a t -> 'b t
(** Preslika vrednosti v slovarju z dano funkcijo, ki poleg vrednosti dobi še
pripadajoči ključ. *)
val vsebuje : kljuc -> 'a t -> bool
(** Preveri, ali slovar vsebuje podan ključ. *)
val za_vse : (kljuc -> 'a -> bool) -> 'a t -> bool
(** Preveri, ali za vse pare ključ, vrednost v slovarju velja podan pogoj. *)
val obstaja : (kljuc -> 'a -> bool) -> 'a t -> bool
(** Preveri, ali obstaja vsaj en par ključ, vrednost v slovarju, ki izpolnjuje
podan pogoj. *)
val v_seznam : 'a t -> (kljuc * 'a) list
(** Pretvori slovar v seznam parov ključ, vrednost v enakem vrstnem redu kot
to določa urejenost. *)
val iz_seznama : (kljuc * 'a) list -> 'a t
(** Ustvari slovar iz seznama parov ključ, vrednost. Če se ključi v seznamu
ponavljajo, naj enak ključ obdrži zadnjo vrednost. *)
end
module MAKE_SLOVAR (U : UREJEN_TIP) : SLOVAR with type kljuc = U.t = struct
type kljuc = U.t
type 'a t = unit
let prazen : 'a t = ()
let dodaj _ _ _ = assert false
let popravi _ _ _ = assert false
let odstrani _ _ = assert false
let velikost _ = assert false
let kljuci _ = assert false
let vrednosti _ = assert false
let najmanjsi_opt _ = assert false
let najvecji_opt _ = assert false
let poisci_opt _ = assert false
let iter _ _ = assert false
let zlozi _ _ _ = assert false
let preslikaj _ _ = assert false
let preslikaji _ _ = assert false
let vsebuje _ _ = assert false
let za_vse _ _= assert false
let obstaja _ _= assert false
let v_seznam _ = assert false
let iz_seznama _ = assert false
end
module SLOVAR_NIZ = MAKE_SLOVAR (STRING_UREJEN_TIP)
let slovar =
SLOVAR_NIZ.iz_seznama
[ ("jabolko", "apple"); ("banana", "banana"); ("cesnja", " cherry") ]
|> SLOVAR_NIZ.dodaj "datelj" "date"
|> SLOVAR_NIZ.odstrani "banana"
|> SLOVAR_NIZ.popravi "cesnja" (function
| None -> Some "cherry"
| Some v -> Some ("sour " ^ v))
|> SLOVAR_NIZ.preslikaj String.length
|> SLOVAR_NIZ.v_seznam
exception NeObstaja
module type SLOVAR =
sig
type kljuc
type 'a t
val prazen : 'a t
val dodaj : kljuc -> 'a -> 'a t -> 'a t
val popravi : kljuc -> ('a option -> 'a option) -> 'a t -> 'a t
val odstrani : kljuc -> 'a t -> 'a t
val velikost : 'a t -> int
val elementi : 'a t -> (kljuc * 'a) list
val kljuci : 'a t -> kljuc list
val vrednosti : 'a t -> 'a list
val najmanjsi : 'a t -> kljuc * 'a
val najmanjsi_opt : 'a t -> (kljuc * 'a) option
val najvecji : 'a t -> kljuc * 'a
val najvecji_opt : 'a t -> (kljuc * 'a) option
val poisci : kljuc -> 'a t -> 'a
val poisci_opt : kljuc -> 'a t -> 'a option
val iter : (kljuc -> 'a -> unit) -> 'a t -> unit
val zlozi : (kljuc -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val preslikaj : ('a -> 'b) -> 'a t -> 'b t
val preslikaji : (kljuc -> 'a -> 'b) -> 'a t -> 'b t
val vsebuje : kljuc -> 'a t -> bool
val za_vse : (kljuc -> 'a -> bool) -> 'a t -> bool
val obstaja : (kljuc -> 'a -> bool) -> 'a t -> bool
val v_seznam : 'a t -> (kljuc * 'a) list
val iz_seznama : (kljuc * 'a) list -> 'a t
end
module MAKE_SLOVAR :
functor (U : UREJEN_TIP) ->
sig
type kljuc = U.t
type 'a t
val prazen : 'a t
val dodaj : kljuc -> 'a -> 'a t -> 'a t
val popravi : kljuc -> ('a option -> 'a option) -> 'a t -> 'a t
val odstrani : kljuc -> 'a t -> 'a t
val velikost : 'a t -> int
val elementi : 'a t -> (kljuc * 'a) list
val kljuci : 'a t -> kljuc list
val vrednosti : 'a t -> 'a list
val najmanjsi : 'a t -> kljuc * 'a
val najmanjsi_opt : 'a t -> (kljuc * 'a) option
val najvecji : 'a t -> kljuc * 'a
val najvecji_opt : 'a t -> (kljuc * 'a) option
val poisci : kljuc -> 'a t -> 'a
val poisci_opt : kljuc -> 'a t -> 'a option
val iter : (kljuc -> 'a -> unit) -> 'a t -> unit
val zlozi : (kljuc -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val preslikaj : ('a -> 'b) -> 'a t -> 'b t
val preslikaji : (kljuc -> 'a -> 'b) -> 'a t -> 'b t
val vsebuje : kljuc -> 'a t -> bool
val za_vse : (kljuc -> 'a -> bool) -> 'a t -> bool
val obstaja : (kljuc -> 'a -> bool) -> 'a t -> bool
val v_seznam : 'a t -> (kljuc * 'a) list
val iz_seznama : (kljuc * 'a) list -> 'a t
end
module SLOVAR_NIZ :
sig
type kljuc = STRING_UREJEN_TIP.t
type 'a t = 'a MAKE_SLOVAR(STRING_UREJEN_TIP).t
val prazen : 'a t
val dodaj : kljuc -> 'a -> 'a t -> 'a t
val popravi : kljuc -> ('a option -> 'a option) -> 'a t -> 'a t
val odstrani : kljuc -> 'a t -> 'a t
val velikost : 'a t -> int
val elementi : 'a t -> (kljuc * 'a) list
val kljuci : 'a t -> kljuc list
val vrednosti : 'a t -> 'a list
val najmanjsi : 'a t -> kljuc * 'a
val najmanjsi_opt : 'a t -> (kljuc * 'a) option
val najvecji : 'a t -> kljuc * 'a
val najvecji_opt : 'a t -> (kljuc * 'a) option
val poisci : kljuc -> 'a t -> 'a
val poisci_opt : kljuc -> 'a t -> 'a option
val iter : (kljuc -> 'a -> unit) -> 'a t -> unit
val zlozi : (kljuc -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val preslikaj : ('a -> 'b) -> 'a t -> 'b t
val preslikaji : (kljuc -> 'a -> 'b) -> 'a t -> 'b t
val vsebuje : kljuc -> 'a t -> bool
val za_vse : (kljuc -> 'a -> bool) -> 'a t -> bool
val obstaja : (kljuc -> 'a -> bool) -> 'a t -> bool
val v_seznam : 'a t -> (kljuc * 'a) list
val iz_seznama : (kljuc * 'a) list -> 'a t
end
val slovar : 'a list = []
Turingovi stroji#
Na predavanjih in vajah smo si ogledali Turingove stroje. Pred vami je neučinkovito implementiran Turingov stroj. Vaša naloga je, da implementacijo s pomočjo slovarjev izboljšate tako, da bo deloval učinkoviteje.
type direction = Left | Right
type state = string
module type TAPE = sig
type t
val make : string -> t
val print : t -> unit
val read : t -> char
val move : direction -> t -> t
val write : char -> t -> t
end
module Tape : TAPE = struct
type t = { left : char list; right : char list }
let make str = { left = []; right = str |> String.to_seq |> List.of_seq }
let print { left; right } =
List.rev_append left right |> List.to_seq |> String.of_seq |> print_endline;
print_endline (String.make (List.length left) ' ' ^ "^")
let read { right } = match right with [] -> ' ' | chr :: _ -> chr
let move dir { left; right } =
match (dir, left, right) with
| Left, ' ' :: left, [] -> { left; right }
| Left, chr :: left, right -> { left; right = chr :: right }
| Left, [], right -> { left = []; right = ' ' :: right }
| Right, [], ' ' :: right -> { left; right }
| Right, left, chr :: right -> { left = chr :: left; right }
| Right, left, [] -> { left = ' ' :: left; right = [] }
let write chr { left; right } =
match right with
| [] when chr = ' ' -> { left; right }
| [] -> { left; right = [ chr ] }
| _ :: right -> { left; right = chr :: right }
end
let primer_trak =
Tape.(
make "ABCDE" |> move Left |> move Left |> move Right |> move Right
|> move Right |> move Right |> write '!' |> print)
module type MACHINE = sig
type t
val make : state -> state list -> t
val initial : t -> state
val add_transition : state -> char -> state -> char -> direction -> t -> t
val step : t -> state -> Tape.t -> (state * Tape.t) option
val run : t -> state -> unit
val speed_run : t -> state -> unit
end
module MachineNeucinkovito : MACHINE = struct
type t = {
initial : state;
transitions : (state * char * state * char * direction) list;
}
let make initial _states = { initial; transitions = [] }
let initial { initial } = initial
let add_transition st chr st' chr' dir tm =
{ tm with transitions = (st, chr, st', chr', dir) :: tm.transitions }
let step tm st tape =
let chr = Tape.read tape in
match
List.find_opt
(fun (st', chr', _, _, _) -> st = st' && chr = chr')
tm.transitions
with
| None -> None
| Some (_, _, st', chr', dir) ->
Some (st', tape |> Tape.write chr' |> Tape.move dir)
let run tm str =
let rec step' (st, tape) =
(Tape.print tape;
print_endline st;
match step tm st tape with
| None -> ()
| Some config' -> step' config')
in
step' (initial tm, Tape.make str)
let speed_run tm str =
let rec step' (st, tape) =
match step tm st tape with
| None -> Tape.print tape
| Some config' -> step' config'
in
step' (initial tm, Tape.make str)
end
type direction = Left | Right
type state = string
module type TAPE =
sig
type t
val make : string -> t
val print : t -> unit
val read : t -> char
val move : direction -> t -> t
val write : char -> t -> t
end
module Tape : TAPE
AB!DE
^
val primer_trak : unit = ()
module type MACHINE =
sig
type t
val make : state -> state list -> t
val initial : t -> state
val add_transition :
state -> char -> state -> char -> direction -> t -> t
val step : t -> state -> Tape.t -> (state * Tape.t) option
val run : t -> state -> unit
val speed_run : t -> state -> unit
end
module MachineNeucinkovito : MACHINE
Sestavite modul MachineUcinkovito, ki učinkovito implementira signaturo
MACHINE z uporabo slovarja, ki ste ga implementirali pred tem. Na kratko
analizirajte časovno zahtevnost operacij add_transition in step v
primerjavi z neučinkovito implementacijo.
Namig:
Za dodatne točke je časovna zahtevnost iskanja prehoda v funkciji
speed_run z nekaj preprocesiranja konstantna.
module MachineUcinkovito : MACHINE = struct
type t = unit
let make _ _ = assert false
let initial _ = assert false
let add_transition _ _ _ _ _ = assert false
let step _ _ _ = assert false
let run _ _ = assert false
let speed_run _ _ = assert false
end
module MachineUcinkovito : MACHINE
Sestavljanje Turingovih strojev#
Sestavite Turingov stroj, ki na vhodnem nizu prepozna palindrom (iz 0 in
1). Če je na vhodnem nizu palindrom, naj na koncu na traku zapiše 1,
sicer 0.
(* let palindrom_stroj : MachineUcinkovito.t = assert false *)
Sestavite Turingov stroj, ki na vhod sprejme niz n enic in na koncu na
traku zapiše n^2 enic.
(* let kvadrat_stroj : MachineUcinkovito.t = assert false *)
Sestavite Turingov stroj, ki na začetku na traku sprejme število n,
zapisano v dvojiškem zapisu, na koncu pa naj bo na traku zapisanih
natanko n enic.
(* let enice_stroj : MachineUcinkovito.t = assert false *)
Sestavite ravno obratni Turingov stroj, torej tak, ki na začetku na traku
sprejme število n enic, na koncu pa naj bo na traku zapisano število n
v dvojiškem zapisu.
(* let dvojski_stroj : MachineUcinkovito.t = assert false *)
Sestavite Turingov stroj, ki na začetku na traku sprejme oklepaje ( in
), [ in ] ter { in }. Stroj naj na traku izpiše 1, če so
oklepaji pravilno uravnoteženi in gnezdeni, ter 0 sicer.
(* let uravnotezeni_oklepaji_stroj : MachineUcinkovito.t = assert false *)