Lista nr 9 (13.12.12)
Część I
Część II

Zadanie 1 (4p.)

Rozważmy język imperatywny IMP, którego składnia zawiera następujące kategorie syntaktyczne:
  • wyrażenia arytmetyczne: a := n | x | plus a a | mult a a
  • wyrażenia logiczne: b := tt | ff | and b b | or b b | not b | equal a a
  • instrukcje: c := assign x a | skip | seq c c | if b then c else c | for x from a to a do c | while b do c
gdzie n oznacza dowolną liczbę całkowitą, a x oznacza dowolną zmienną. Programem w języku IMP nazywamy parę złożoną z jednej instrukcji oraz wyrażenia arytmetycznego. Wykonanie programu (c, a) rozpoczyna się przy pustej pamięci i polega na wykonaniu instrukcji c (według semantyki podanej poniżej), a następnie obliczeniu wartości wyrażenia a przy stanie pamięci otrzymanym jako wynik wykonania instrukcji c. Semantyka wyrażeń arytmetycznych i logicznych jest standardowa, a ich ewaluacja (tj. obliczanie wartości) nie zmienia stanu pamięci. Odwołanie się do pamięci, w której nie ma wartości szukanej zmiennej powoduje natychmiastowe przerwanie obliczenia i rzucenie wyjątku.

Napisz w Ocamlu interpreter języka IMP według następujących wskazówek:
  1. zdefiniuj typy danych: aexp dla wyrażeń arytmetycznych, bexp dla wyrażeń logicznych i cmd dla instrukcji
  2. zdefiniuj typ zmiennych jako string: type var = string
  3. niech mem będzie typem stanów pamięci; interpreter może odwoływać się do pamięci wyłącznie za pomocą funkcji lookup : mem -> var -> int (do wyszukiwania wartości zmiennej w pamięci) oraz update : mem -> var -> int -> mem (do zmiany wartości zmiennej w pamięci)
  4. zaimplementuj powyższe funkcje dostępu do pamięci przyjmując mem = var -> int (pamięć jest funkcją przyporządkowującą zmiennym ich wartości) oraz zdefiniuj pustą pamięć empty_mem : mem jako funkcję rzucającą wyjątek dla dowolnego argumentu
  5. napisz funkcję aeval ewaluacji wyrażeń arytmetycznych
  6. napisz funkcję beval ewaluacji wyrażeń logicznych
  7. napisz funkcję exec interpretującą instrukcje według semantyki podanej poniżej
  8. napisz funkcję run interpretującą program
  9. po zrealizowaniu powyższych punktów napisz w języku IMP program obliczania silni i przetestuj na nim swój interpreter
Semantyka instrukcji:
(a,σ) → n
-------------------------------
(assign x a, σ) → σ[x:=n]
-------------------------------
(skip , σ) → σ
(c1,σ) → σ'    (c2,σ') → σ''
--------------------------------
(seq c1 c2, σ) → σ''
(b,σ) → tt    (c1,σ) → σ'
--------------------------------
(if b then c1 else c2, σ) → σ'
(b,σ) → ff    (c2,σ) → σ'
--------------------------------
(if b then c1 else c2, σ) → σ'
(b,σ) → tt   (c,σ) → σ'   (while b do c,σ') → σ''
--------------------------------
(while b do c, σ) → σ''
(b,σ) → ff
--------------------------------
(while b do c, σ) → σ
(a1,σ) → n    (a2,σ) → m    Repeat (x,n,m,c,σ,σ')
--------------------------------
(for x from a1 to a2 do c, σ) → σ'
n > m
--------------------------------
Repeat (x,n,m,c,σ,σ)
n <= m   (c,σ[x:=n]) → σ'   Repeat (x,n+1,m,c,σ',σ'')
--------------------------------
Repeat (x,n,m,c,σ,σ'')

Zadanie 2 (8p.)

Rozważmy typ danych do reprezentacji termów w rachunku lambda (ze zmiennymi jako napisami):
  type term = 
      Var of string 
    | Abs of string * term 
    | App of term * term 
[6p.] Napisz dwa ewaluatory implementujące semantykę małych kroków dla termów zamkniętych w tym języku: jeden stosujący strategię gorliwą (definicja relacji ->v poniżej), a drugi leniwą (definicja relacji ->n poniżej).
s jest wartością
---------------------------
(\x.t) s ->v t[x:=s]
t ->v t'
---------------------
t s ->v t' s
s ->v s' i t jest wartością
---------------------
t s ->v t s'
---------------------------
(\x.t) s ->n t[x:=s]
t ->n t'
---------------------
t s ->n t' s
t[x:=s] oznacza podstawienie termu s pod wolne wystąpienia zmiennej x w termie t. Aby zapewnić poprawność operacji podstawienia, żadna zmienna wolna w s nie może występować jako zmienna związana w termie t. W przypadku jednak, gdy rozważamy wyłącznie termy zamknięte (tj. bez wolnych zmiennych), w trakcie ich ewaluacji ten problem nie wystąpi.
Wartością nazywamy taki term, który nie redukuje się do innego termu; np. dla relacji ->v wartością jest każdy taki term t, dla którego nie istnieje term s taki, że t ->v s.
Przetestuj ewaluatory. Znajdź przykłady termów, dla których działanie obu ewaluatorów się różni (dają w wyniku różne wartości; jeden się zatrzymuje, a drugi nie).
[2p.] Rozszerz język i ewaluatory o termy logiczne true, false oraz konstrukcję warunkową if o standardowej semantyce. Jak zmieni się zbiór wartości dla obu relacji?
Lista nr 8 (06.12.12)
Część I
Część II

Zadanie 1 (4p.)

Definiujemy typ danych do reprezentacji drzew binarnych przechowujących wartości w liściach:
type 'a btree = Leaf of 'a | Node of 'a btree * 'a btree
Dwa drzewa binarne typu t btree mają jednakowe korony (zakładamy, że elementy typu 'a są porównywalne), jeśli listy utworzone przez odczytanie wartości w ich liściach od lewej do prawej są równe. Na przykład drzewa
Node (Node (Leaf 1, Leaf 2), Leaf 3)
i
Node (Leaf 1, Node (Leaf 2, Leaf 3))
mają jednakowe korony, równe [1; 2; 3].
  1. (1p.) Napisz funkcję rozstrzygającą czy dwa drzewa mają jednakowe korony, bazując bezpośrednio na definicji i nie dbając o efektywność rozwiązania.
  2. (3p.) Wykorzystując pojęcie odroczonego obliczenia, napisz efektywną i czysto funkcyjną wersję funkcji samefringe, tj. taką, która przerywa obliczenia w momencie napotkania pierwszej różnicy między koronami drzew. Podpowiedź: należy odraczać trawersowanie prawego poddrzewa.

Zadanie 2 (4p.)

Rozwiąż zadanie kontrolne do Wykładu 8 (gra nim w Haskellu).

Zadanie 3 (6p.)

Przeanalizuj interpreter Prologa zamieszczony tutaj. Rozważmy typ danych do reprezentowania wyrażeń regularnych:
  type regexp = 
  | Atom of char
  | And of regexp * regexp  (*  r1r2     *)
  | Or of regexp * regexp   (*  r1 | r2  *)
  | Star of regexp          (*  r*  *)
Bazując na modelu obliczeń z nawrotami przy użyciu kontynuacji sukcesu oraz kontynuacji porażki, zaprezentowanym na przykładzie interpretera Prologa, napisz funkcje
  match_regexp : regexp -> char list -> (char list -> (unit -> 'a) -> 'a) -> (unit -> 'a) -> 'a
oraz
  run : regexp -> char list -> bool
które dla danego wyrażenia regularnego implementują niedeterministyczny automat rozpoznający język opisany przez to wyrażenie.
Lista nr 7 (29.11.12)
Część I

Zadanie 1 (5p.)

Rozwiąż zadanie 1 z listy zadań kontrolnych do wykładu 7 (4p.). Następnie przetestuj implementację z punktu c) używając jej do napisania funkcji przechodzenia wszerz drzewa binarnego (1p.).

Zadanie 2 (2p.)

Rozważmy modyfikowalne listy zdefiniowane następująco:
  type 'a list_mutable = LMnil | LMcons of 'a * 'a list_mutable ref
Zaimplementuj konkatenację list typu 'a list_mutable na dwa sposoby:
  1. funkcja concat_copy buduje listę wynikową kopiując pierwszy argument;
  2. funkcja concat_share buduje listę wynikową bez kopiowania argumentów.
Skonstruuj odpowiednie przykłady testujące implementację.
Część II

Zadanie 3 (4p.)

Technika memoizacji pozwala wykorzystać cechy imperatywne języka w celu zwiększenia efektywności działania funkcji, która sama jest czysto funkcyjna, tj. kolejne wywołanie takiej funkcji dla tego samego argumentu zwróci tę samą wartość. Memoizacja polega na zapamiętywaniu wartości wywołań funkcji dla konkretnych argumentów w pewnej strukturze danych, i na wyszukiwaniu potrzebnych wartości przy kolejnych wywołaniach tej funkcji. Aby umożliwić memoizację dowolnej funkcji, zaimplementuj następujący schemat:
  • zdefiniuj typ polimorficzny ('a, 'b) memo służący jako tablica wartości wywołań dowolnej funkcji
  • napisz funkcję tworzenia pustej tablicy create_memo : unit -> ('a, 'b) memo
  • napisz funkcję wyszukiwania w tablicy wartości funkcji dla zadanego argumentu memo_find : ('a, 'b) memo -> 'a -> 'b option
  • napisz funkcję dopisującą do tablicy nową wartość wywołania funkcji memo_add : ('a, 'b) memo -> 'a -> 'b -> unit
  • napisz funkcję memo_f : ('a -> 'b) -> 'a -> 'b, która dla zadanej jako argument funkcji f : 'a -> 'b zwraca funkcję, która działa tak samo jak f, ale wykorzystuje tablicę memoizacji, więc jest potencjalnie bardziej efektywna

Wykorzystaj ten schemat do memoizacji funkcji wyznaczającej kolejne liczby Fibonacciego: napisz funkcję fib : int -> int według standardowej definicji oraz funkcję fib_memo = memo_f fib wykorzystującą memoizację. Porównaj czasy wykonania obu funkcji (użyj funkcji Sys.time).
Następnie zauważ, że ogólny schemat memoizacji nie uwzględnia faktu, że fib jest funkcją rekurencyjną. Napisz ulepszoną funkcję fib_memo' : int -> int, która wykorzystuje jawną memoizację, i porównaj czasy wykonań z fib_memo.

Zadanie 4 (4p.)

Rozważmy sygnaturę dla funkcyjnych kolejek priorytetowych:
module type PQUEUE =
sig

  type priority
  type 'a t
  
  exception EmptyPQueue
  
  val empty : 'a t
  val insert : 'a t -> priority -> 'a -> 'a t
  val remove : 'a t -> priority * 'a * 'a t
    
end
  1. (1p.) Zdefiniuj moduł PQueue : PQUEUE, przyjmując typ priority = int. Reprezentacja kolejki może być dowolna.
  2. (1p.) Wykorzystaj moduł PQueue do napisania funkcji sortowania list liczb typu int.
  3. (2p.) Uogólnij rozwiązanie punktów 1 i 2 definiując funktor, który dla zadanego modułu OrdType : ORDTYPE zwraca moduł o sygnaturze PQUEUE, gdzie
    module type ORDTYPE =
    sig
      type t
      val compare : t -> t -> int
    end
    
    Zmodyfikuj odpowiednio funkcję sortowania list z p. 2 i przetestuj ją.
Lista nr 6 (22.11.12)
Część I

Zadanie 1 (2p.)

Napisz funkcję next, która produkuje kolejne wartości silni w następujący sposób:
# next();;
- : int = 1
# next();;
- : int = 2
# next();;
- : int = 6
# next();;
- : int = 24
itd... oraz funkcję reset, która ustawia początkową wartość argumentu dla następnych wywołań funkcji next, np.
# next();;
- : int = 120
# reset();;
- : unit = ()
# next();;
- : int = 1
# next();;
- : int = 2
Uwaga! Funkcje nie mogą wykorzystywać żadnych zmiennych globalnych.

Zadanie 2 (4p.)

Rozważmy typ danych comp służący do reprezentowania obliczeń:
	type 'a computation = 
	| Value of 'a
	| Delayed of (unit -> 'a)

	type 'a comp = 'a computation ref
      
Wykonanie obliczenia implementuje funkcja compute : 'a comp -> 'a zdefiniowana następująco:
	let compute c =
	match !c with 
	| Value a -> a
	| Delayed t -> let a = t() in c := Value a; a
      
Zdefiniujmy teraz typ strumieni wykorzystujący takie obliczenia:
type 'a stream = Cons of 'a * 'a stream computation ref
Zdefiniuj funkcje:
  • map : ('a -> 'b) -> 'a stream -> 'b stream
  • take : int -> 'a stream -> 'a list
działające tak, jak ich odpowiedniki na listach, oraz inne funkcje przydatne do testowania tak zdefiniowanych strumieni.
Następnie zdefiniuj strumienie:
  • ones złożony z samych jedynek,
  • oraz nats złożony z kolejnych liczb naturalnych.
Część II

Zadanie 3 (4p.)

Definiujemy typ danych do reprezentacji drzew binarnych przechowujących wartości zarówno w węzłach jak i w liściach:
type 'a btree = Leaf of 'a | Node of 'a btree * 'a * 'a btree
  1. Napisz funkcję numerującą węzły i liście drzewa binarnego w kolejności przechodzenia go w głąb (preorder). Na przykład, tak ponumerowaną wersją drzewa
    Node (Node (Leaf 'a', 'b', Leaf 'c'), 'd', Leaf 'e')
    jest
    Node (Node (Leaf 3, 2, Leaf 4), 1, Leaf 5).
  2. Napisz funkcję numerującą węzły i liście drzewa binarnego w kolejności przechodzenia go wszerz. Na przykład, tak ponumerowaną wersją drzewa
    Node (Node (Leaf 'a', 'b', Leaf 'c'), 'd', Leaf 'e')
    jest
    Node (Node (Leaf 4, 2, Leaf 5), 1, Leaf 3).

Zadanie 4 (4p.)

Dalszy ciąg zadania 2: zdefiniuj strumień złożony z kolejnych liczb o rozkładzie 2i*3j*5k (i, j, k - naturalne) w porządku rosnącym i bez powtórzeń.
Lista nr 5 (13.11.12)
Część I
W poniższych zadaniach wykorzystaj typ do reprezentacji leniwych list (strumieni) zdefiniowany na wykładzie.

Zadanie 1 (2p.)

  1. Zdefiniuj funkcję generującą nieskończony strumień o okresie zadanym jako zwykła lista skończona, np. dla okresu [0;1] ma powstać strumień o elementach 0,1,0,1,...
  2. Zdefiniuj funkcję, która dla zadanej listy leniwej ll generuje listę leniwą, w której element stojący na i-tym miejscu w liście ll jest powtórzony i razy, np. dla listy leniwej o elementach 1,2,3,4, powinna powstać lista o elementach 1,2,2,3,3,3,4,4,4,4.

Zadanie 2 (4p.)

  1. Zdefiniuj strumień obliczający wartość liczby pi z rosnącą dokładnością, korzystając ze wzoru Leibniza: π/4 = 1 - 1/3 + 1/5 - 1/7 + ...
  2. Napisz funkcję przekształcającą dowolny strumień x1,x2,x3,... w strumień postaci f x1 x2 x3, f x2 x3 x4, f x3 x4 x5,..., dla dowolnej funkcji f.
  3. Korzystając z powyższych definicji i transformacji Eulera:
    F x y z = z - (y-z)2/(x-2y+z)
    utwórz nowy strumień, szybciej zdążający do liczby π.
  4. Znajdź inny wzór przybliżający liczbę π, zdefiniuj odpowiedni strumień i porównaj z wcześniej zdefiniowanymi strumieniami.
Część II

Zadanie 3 (4p.)

Wykorzystując schemat algorytmu z nawrotami podany na wykładzie, napisz funkcję generate_palindromes generującą wszystkie palindromy złożone z zadanych liter. Przykładowo, wywołanie funkcji ltake (10, generate_palindromes ["a";"b";"c"]) powinno dać w wyniku listę: [ []; ["a"]; ["b"]; ["c"]; ["aa"]; ["bb"]; ["cc"]; ["aaa"]; ["aba"]; ["aca"]]

Zadanie 4 (2p.)

Napisz program rozwiązujący problem skoczka szachowego: w jaki sposób skoczek może obejść całą szachownicę o wymiarach n x n tak, by odwiedzić każde pole dokładnie raz? Rozwiązaniem powinna być lista kolejnych pozycji na szachownicy odwiedzonych przez skoczka. Wykorzystaj schemat algorytmu z nawrotami omówiony na wykładzie.
Lista nr 4 (08.11.12)
Część I

Zadanie 1 (2p.)

Napisz funkcję odwracania list zagnieżdżonych zdefiniowanych na wykładzie. Funkcja powinna odwracać każdą listę zagnieżdżoną w innej liście, np. reverse (N (2, N (3, NN (N (4, N (5, Nil)), Nil)))) powinno zwracać NN (NN (Nil, N (5, N (4, Nil))), N (3, N (2, Nil))), a reverse (N (2, Nil)) powinno zwracać N (2, Nil).

Zadanie 2 (4p.)

Rozważmy typ danych dla drzew binarnych, zdefiniowany następująco:

type 'a btree = Leaf | Node of 'a btree * 'a * 'a btree

Mówimy, że drzewo jest zbalansowane, jeśli dla każdego węzła v liczby węzłów w lewym i prawym poddrzewie zakorzenionym w v różnią się co najwyżej o 1.
  1. [1p.] Napisz funkcję, która dla zadanej listy etykiet tworzy zbalansowane drzewo, dla którego tę listę można otrzymać przechodząc je w porządku preorder.
  2. [1p.] Napisz funkcję, która dla zadanej listy etykiet tworzy zbalansowane drzewo, dla którego tę listę można otrzymać przechodząc je w porządku inorder.
  3. [2p.] Napisz funkcję, która dla zadanej listy etykiet konstruuje wszystkie drzewa, których przejście w porządku preorder daje tę listę.
Przetestuj funkcje na przykładach.

Zadanie 3 (6p., dla chętnych)

Zdefiniuj w Ocamlu operator punktu stałego Y, a raczej taki jego wariant, który się typuje (powinien być typu (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b ) i działa poprawnie. Problem z typowaniem można rozwiązać używając typu rekurencyjnego. Wykorzystaj ten operator do zdefiniowania funkcji liczącej np. silnię. Jaka jest różnica, gdy próbujemy to samo zrobić w Haskellu?
Część II

Zadanie 4 (6p.)

Zdefiniuj typ służący do reprezentacji formuł rachunku zdań składających się ze zmiennych zdaniowych, negacji, koniunkcji i alternatywy.
  • Napisz (pierwszą) funkcję sprawdzającą, czy dana formuła jest tautologią. W tym celu należy generować kolejne wartościowania zmiennych zdaniowych występujących w danej formule i sprawdzać dla nich wartość formuły. W przypadku, gdy formuła nie jest tautologią, funkcja powinna - oprócz tej informacji - podać jedno z wartościowań, dla których formuła nie jest prawdziwa.
  • W podobny sposób napisz funkcje sprawdzające, czy formuła jest sprzeczna i czy jest spełnialna.
  • Napisz funkcję, która przekształca zadaną formułę w formułę jej równoważną w koniunkcyjnej postaci normalnej.
  • Napisz (drugą) funkcję sprawdzającą (czysto syntaktycznie!), czy zadana formuła jest tautologią, korzystając z faktu, że każdą formułę można przedstawić w koniunkcyjnej postaci normalnej.
  • Napisz funkcję, która przekształca zadaną formułę w formułę jej równoważną w dyzjunkcyjnej postaci normalnej.
  • Napisz (trzecią) funkcję sprawdzającą (czysto syntaktycznie!), czy zadana formuła jest sprzeczna, korzystając z faktu, że każdą formułę można przedstawić w dyzjunkcyjnej postaci normalnej.
  • Zadbaj, by wyświetlanie formuł i wartościowań było czytelne - napisz odpowiednie funkcje konwersji i uźywaj ich do wyświetlania wyników.
  • Przygotuj 5 różnych przykładów formuł (z 1,2,3 zmiennymi zdaniowymi) i przetestuj funkcje.
Lista nr 3 (25.10.12)
Część I
Zapoznaj się ze wskazówkami dotyczącymi dobrego stylu programowania w OCamlu i stosuj je.

Zadanie 1 (5p.)

Niech macierz kwadratowa n x n będzie reprezentowana wierszami jako lista list, np. lista l = [[1.;2.;3.];[4.;5.;6.];[7.;8.;9.]] reprezentuje macierz
123
456
789
  1. Napisz funkcję, która dla zadanej macierzy kwadratowej i liczby naturalnej n wyznacza n-tą kolumnę macierzy.
  2. Napisz funkcję transpozycji macierzy; transpozycja macierzy l jest reprezentowana jako lista [[1.;4.;7.];[2.;5.;8.];[3.;6.;9.]].
  3. Napisz funkcję zipf, która dla danych dwóch list typów 'a list i 'b list i funkcji dwuargumentowej f typu 'a -> 'b -> 'c tworzy listę złożoną z wartości funkcji f na argumentach z obu list położonych na tych samych pozycjach, np. zipf ( +. ) [1.;2.;3.] [4.;5.;6.] = [5.;7.;9.].
  4. Wykorzystując funkcję zipf napisz funkcję mult_vec, która oblicza iloczyn zadanego wektora i zadanej macierzy, np. mult_vec [1.;2.] [[2.;0.];[4.;5.]] = [10.;10.].
  5. Korzystając z funkcji mult_vec napisz funkcję mnożenia dwóch macierzy kwadratowych tego samego rozmiaru.
Powyższe funkcje napisz korzystając z funkcji z modułu List, unikając używania konstrukcji let rec.

Zadanie 2 (2p.)

Napisz funkcję znajdującą wszystkie permutacje listy zadanej jako argument.
Część II

Zadanie 3 (3p.)

Napisz funkcję, która dla zadanej listy - traktowanej jako permutacja pewnych elementów - znajduje kolejną permutację tych samych elementów w porządku leksykograficznym, np. dla listy [2;3;1] zadanej jako argument, funkcja powinna zwrócić listę [3;1;2], a dla listy ['a';'b';'e';'a'] wynikiem powinna być lista ['a';'e';'b';'a']. W przypadku, w którym zadana permutacja jest największa, funkcja powinna zwracać permutację najmniejszą.

Zadanie 4 (3p.)

Napisz funkcję group, która grupuje w listę sąsiednie wystąpienia tego samego elementu w zadanej liście początkowej (i zwraca listę list).
Przykład: wywołanie group [1;2;2;3;3;3;4] powinno zwrócić listę [[1];[2;2];[3;3;3];[4]].
Lista nr 2 (18.10.12)
Część I
Znajdź implementacje ocamlowych funkcji map, rev i flatten z modułu List. Czy wykorzystują rekursję ogonową?

Zadanie 1 (2p.)

Przetestuj działanie funkcji map na listach o różnej długości, a następnie napisz i przetestuj jej lepszą wersję, która nie powoduje przepełnienia stosu dla bardzo dużych list (np. o 1 000 000 elementów).

Zadanie 2 (2p.)

Podobnie jak w Zadaniu 1, napisz i przetestuj funkcję flatten, która działa na dużych listach.

Zadanie 3 (2p.)

Stosując rekursję ogonową, napisz funkcję obliczającą n-ty wyraz ciągu zdefiniowanego wzorem:
  • a0 = 1
  • a1 = 2
  • an = 2 * an-2 - an-1 + 1 dla n>1
Część II

Zadanie 4 (3p.)

  • Napisz funkcję merge, która łączy dwie listy posortowane rosnąco w pewnym porządku <= tak, by wynik działania funkcji był także listą posortowaną rosnąco w tym samym porządku. Argumentami funkcji merge powinny być: funkcja cmp typu 'a -> 'a -> bool (zakładamy, że cmp a b = true, gdy a<=b), i dwie listy elementów typu 'a.
    Przykład: merge (<=) [1;2;5] [3;4;5] utworzy listę [1;2;3;4;5;5].
  • Zapisz tę funkcję używając rekursji ogonowej i skonstruuj przykład pokazujący różnicę w efektywności tych dwóch definicji.
  • Wykorzystaj funkcję merge do napisania funkcji sortowania przez scalanie.

Zadanie 5 (3p.)

Załóżmy, że wielomiany o współczynnikach rzeczywistych są reprezentowane jako listy współczynników od najwyższej potęgi do najniższej, np. [1.;0.;-1.;2.] oznacza wielomian x3-x+2 (wielomian zerowy reprezentujemy jako listę [0.]). Napisz funkcję, która dla zadanej listy reprezentującej wielomian i dla danego argumentu x typu float, obliczy wartość tego wielomianu w punkcie x. Napisz tę funkcję w dwóch wersjach: raz za pomocą rekursji ogonowej, a następnie bez jawnego użycia rekurencji, korzystając z odpowiedniej funkcji bibliotecznej modułu List.
Lista nr 1 (11.10.12)
Część I

Zadanie 1 (0p.)

  • Dla każdego z typów int, float, char, string, bool oraz unit napisz wyrażenie tego typu i wykorzystaj interpreter OCamla (program ocaml, z linii poleceń lub zintegrowany z emacsem) do obliczenia jego wartości.
  • Skompiluj i wykonaj przykładowy program za pomocą kompilatorów ocamlc i ocamlopt (por. Ocaml Manual, Part III).

Zadanie 2 (0p.)

Czy następujące wyrażenia są dobrze typowane? Sprawdź ich typy i wartości w interpreterze.
  • if false then 1 else 3.5
  • false || "ab">"cd"
  • x = 5
  • if true then ()
  • let z = 3 and y = z + 4 in y * z
  • let x = 2 in x ^ "aa"
  • let y = "abc" in y ^ y
  • (fun x -> x.[1]) "abcdef"
  • (fun x -> x) true
  • let x = [1;2] in x @ x
  • let rec f f = f + f in f 42

Zadanie 3 (0p.)

Zidentyfikuj zmienne wolne i związane w wyrażeniach:
  • let x = x in x ^ x
  • let x = 10. in let y = x ** 2. in y *. x
  • let x = 1 and y = x in x + y
  • let x = 1 in fun y z -> x * y * z

Zadanie 4 (0p.)

Anonimową funkcję dodawania dwóch liczb całkowitych możemy zapisać za pomocą wyrażenia fun x y -> x + y. Zdefiniuj tę funkcję nadając jej identyfikator plus na 2 różne (składniowo) sposoby. Następnie korzystając z jednej z tych definicji napisz funkcję plus_3 dodawania 3 do dowolnej liczby całkowitej.

Zadanie 5 (3p.)

Jaki jest typ wyrażenia fun x -> x? Napisz wyrażenie anonimowe reprezentujące funkcję identycznościową, którego typem w OCamlu jest typ int -> int.
Napisz dowolne wyrażenie typu ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b oraz dowolne wyrażenie typu 'a -> 'b.

Zadanie 6 (3p.)

Napisz funkcję definiującą złożenie dwóch funkcji oraz funkcję iterowania wywołania funkcji wykorzystującą funkcję złożenia. Za pomocą iteracji zdefiniuj mnożenie i potęgowanie.
Część II

Zadanie 7 (6p.)

Strumień (tj. nieskończony ciąg) liczb całkowitych możemy reprezentować za pomocą funkcji typu int -> int w taki sposób, że dla dowolnej takiej funkcji s, s 0 oznacza pierwszy element strumienia, s 1 następny, itd.
Używając powyższej reprezentacji zdefiniuj następujące funkcje na strumieniach (tam, gdzie to możliwe, funkcje te powinny być polimorficzne, tj. powinny działać na strumieniach o elementach dowolnego typu):
  • hd, tl - funkcje zwracające odpowiednio głowę (pierwszy element) i ogon strumienia (strumień zaczynający się od drugiego elementu)
  • add - funkcja dodawania zadanej stałej do każdego elementu strumienia i zwracająca powstały w ten sposób strumień
  • map - funkcja, która dla zadanej operacji 1-argumentowej przetwarza elementy zadanego strumienia za pomocą tej operacji i zwraca powstały w ten sposób nowy strumień (tak, jak map na listach skończonych)
  • map2 - jak wyżej, ale dla zadanych: funkcji 2-argumentowej i 2 strumieni
  • replace - funkcja, która dla zadanego indeksu n, wartości a i strumienia s zastępuje co n-ty element strumienia s przez wartość a i zwraca powstały w ten sposób strumień
  • take - funkcja, która dla zadanego indeksu n i strumienia s tworzy nowy strumień złożony z co n-tego elementu strumienia s
  • fold - funkcja, która dla zadanej funkcji f:'a->'b->'a, wartości początkowej a:'a i strumienia s elementów typu 'b tworzy nowy strumień, którego każdy element jest wynikiem "zwinięcia" początkowego segmentu strumienia s aż do bieżącego elementu włącznie za pomocą funkcji f, tj. w strumieniu wynikowym element o indeksie n ma wartość (... (f (f a s_0) s_1)... s_n)
  • tabulate - funkcja tablicowania strumienia, której wartością powinna być lista elementów strumienia leżąca w zadanym zakresie indeksów
Zdefiniuj strumień złożony z kolejnych liczb Fibonacciego wykorzystując niektóre z powyższych funkcji i przetestuj implementację.
W definicji funkcji tabulate wykorzystaj możliwość definiowania parametrów opcjonalnych dla funkcji (niech początek zakresu indeksów będzie opcjonalny i domyślnie równy 0).
Przykład. Pisząc let f ?(x=0) y = x + y deklarujemy, że pierwszy argument funkcji f o etykiecie x jest opcjonalny, a jego wartość domyślna wynosi 0. Funkcję f można zatem wywołać za pomocą wyrażenia f 3 (= 3) lub jawnie podając wartość parametru opcjonalnego, za pomocą składni f ~x:42 3 (= 45).