Uwaga. Do uruchomienia i przetestowania rozwiązań poniższych zadań użyj środowiska Node.js. Zweryfikuj poprawność kodu każdego z rozwiązań za pomocą narzędzia JSHint z domyślnym zestawem opcji.
Przed weryfikacją, na początku kodu umieść następujące dwie linijki:
/*jshint globalstrict: true */
'use strict';
Powyższy zabieg wymusza stosowanie „sensownego podzbioru” JS. Opcja JSHint globalstrict: true
pozwala użyć pojedynczą
frazę use strict
dla całego skryptu. Takie podejście wiąże się z pewnym niebezpieczeństwem, ale podczas
dzisiajeszych ćwiczeń nie będziemy się tym przejmowali.
Jeśli używasz polecenia console.log(…)
to dodać musisz jeszcze jedną opcję JSHint:
/*jshint globalstrict: true, devel: true */
'use strict';
Zadanie 1. (Weryfikacja typów w JS „dla ubogich”)
Korzystając z tego, że funkcje w języku JavaScript są również obiektami zdefiniuj dwie funkcje:
defFun(fun, types)
taką, że:- jej argumentami są: funkcja fun oraz tablica typów types
- wynikiem jest funkcja fun „wzbogacona” (jako obiekt) o atrybut o nazwie typeConstr, którego wartością jest tablica types
var myfun = defFun(function (a, b) { return a + b; }, ['number', 'number']);
appFun(f, arg1, arg2, … )
taką, że:- dla funkcji f (w zamyśle – zdefiniowanej za pomocą defFun) oraz argumentów arg1, arg2,… sprawdza ona (o ile funkcja wyposażona jest w atrybut typeConstr) poprawność typów argumentów, a następnie ją do nich stosuje.
- jeśli któryś z argumentów ma niepoprawny typ (bądź argument
f
nie posiada atrybututypConstr
), wówczas funkcjaappFun
powinna rzucić wyjątek w postaci literału obiektowego, zawierającego atrybuttyperr
o wartości napisowej zawierającej szczegóły napotkanego błędu (np. który argument spowodował błąd, czego się spodziewaliśmy, a co napotkaliśmy):throw({ typerr: "…" });
Przetestuj działanie swojego rozwiązania stosując np. konstrukcję:
try {
console.log(appFun(myfun, 12, 15));
} catch (e) {
console.log(e.typerr);
}
Wskazówki:
- Przypomnijmy, że zastosowanie funkcji
f
do ciągu argumentówa1,…,an
zamiastf(a1,…,bn)
można też uzyskać przez:f.apply(this, [a1,…,an])
(rola argumentuthis
wyjaśniona została na wykładzie). - W rozwiązaniu może przydać się „pseudo-tablica” arguments oraz standardowe metody operujące na tablicach (np. push, czy slice). W przypadku chęci zastosowania metody slice należy pamiętać o tym, że arguments nie jest tablicą, a jedynie obiektem tablicopodobnym.
Zadanie 2. (Wyrażenia regularne)
Stwórz metodę o nazwie nbsp
, która dla dowolnego
łańcucha znaków (obiektu „typu String”)
var tekst = 'Ala i As poszli w las';
powoduje zamianę odstępów występujących pomiędzy
„samotnymi” znakami ze zbioru
{a, i, o, u, w, z} i następującymi po nich
fragmentami tekstu, na „niełamliwą spację”
języka HTML (
).
W zamyśle, ma to zapobiec dzieleniu
przez przeglądarkę wierszy
w sposób niezgodny z przyjętymi dla języka
polskiego zasadmi typograficznymi.
console.log(tekst.nbsp());
// efekt: Ala i As poszli w las
Uwaga. Pamiętaj, że łańcuch znaków może też zawierać zmiany wiersza, które również należy traktować jako odstępy.
Zadanie 3. (Wyrażenia regularne)
Rozważmy następujący fragment „szablonu” dokumentu HTML-owego
var szablon =
'<table border="{border}">' +
' <tr><td>{first}</td><td>{last}</td></tr>' +
'</table>';
Napisz funkcję podstaw
, która dla dowolnego szablonu powyższej postaci oraz dowolnego obiektu, np. postaci:
var dane = {
first: "Jan",
last: "Kowalski",
pesel: "97042176329"
};
dokona podstawienia: za parametr {param}
– wartość atrybutu o nazwie param
.
Jeśli w obiekcie nie wystąpi atrybut o potrzebnej nazwie, to parametr powinien pozostać w szablonie. Wywołanie powinno mieć postać:
szablon.podstaw(dane);
Dla przedstawionych powyżej danych wynikiem powinien być kod:
<table border="{border}">
<tr><td>Jan</td><td>Kowalski</td></tr>
</table>
Zadanie 4. (Ciąg Fibonacciego z zapamiętywaniem)
Ciąg Fibonacciego zdefiniować można za pomocą funkcji:
var fib = function fib(arg) {
if (arg <= 0) {
return 0;
}
if (arg === 1) {
return 1;
}
return fib(arg - 1) + fib(arg - 2);
};
Uzupełnij definicję funkcji
memo
var memo = function (cache, fun) {
...
};
tak, aby dla dowolnej liczby naturalnej n zachodziła równość
fibonacci(n) === fib(n)
gdzie funkcja fibonacci zdefiniowana jest następująco:
var fibonacci = memo([0, 1], function (recur, n) {
return recur(n - 1) + recur(n - 2);
});
Pierwszy argument funkcji memo
jest więc, jak widać,
tablicą przechowującą już obliczone wartości funkcji. Pozwala on
uniknąć wielokrotnego powtarzania już raz wykonanych obliczeń.
Różnicę w wydajności obu sposobów obliczania wyrazów ciągu Fibonacciego możesz łatwo zaobserwować, próbując wywołać obie funkcje np. dla wartości >30.
Zadanie 5. (funkcja oceny)
Dane są dwa ciągi liczb o jednakowej długości – Z[1..N]
i R[1..N]
. Napisz funkcję
var ocena = function (zadanie, ruch) {
// ...
return wynik;
};
Oba ciągi liczb należy porównać i „ocenić” według podanej poniżej zasady.
- Ocenę ruchu stanowi ciąg „czarnych” i „białych” punktów.
- Dla każdej pozycji k ciągu
R[1..N]
porównujemy wartościR[k]
orazZ[k]
. Jeśli są one identyczne, do wyniku dodajemy jeden „czarny punkt”, a elementyZ[k]
iR[k]
oznaczamy jako „ocenione”. - Następnie, dla każdego nie ocenionego jeszcze elementu
R[k]
w ciaguR[1..N]
, sprawdzamy, czyR[k]
występuje wśród nie ocenionych elementów ciąguZ[1..N]
. Jeśli znajdziemy taki element, np.Z[m]
, to do oceny dodajemy jeden „biały punkt”, a elementyR[k]
iZ[m]
oznaczamy jako „ocenione”. - Ocena kończy się z chwilą oznaczenia wszystkich elementów ciągu
R[1..N]
jako „ocenione”. Odpowiedź stanowi wyliczony ciąg „czarnych” i „białych” punktów.
W swoim rozwiązaniu nie używaj instrukcji pętli (for
ani while
). Zamiast nich wykorzystaj
oferowane przez język JavaScript metody tablicowe.