Każdy z nas ma różne spojrzenie na świat, na politykę, na społeczeństwo, na miłość, przyjaźń, rodzinę …. można by wymieniać tak długo. Czasami spotkamy się w punkcie wspólnym z innymi, czasem się miniemy.
W zależności od zdobytych doświadczeń i założonego celu stosujemy też różne strategie. A w kodowaniu jest jak w życiu ![]()
Posłużę się tutaj prostym przykładem.
Załóżmy, że chcemy na konsoli wypisać 'a' jeśli b > 0 lub 'c' jeśli jest mniejsza.
Najprostszym sposobem na to zadanie jest zrobić prostego if’a
1
2
if(b > 0) console.log('a')
else console.log('c')
Drugim podejściem jest zastosowanie operatora warunkowego ternary operator (ang) np.:
1
console.log(b > 0 ? 'a' : 'c')
Możemy też jednak użyć zupełnie innej strategii ![]()
1
console.log('ca'[(b > 0) * 1])
Wygląda jak by ktoś coś pokręcił? Nic bardziej mylnego.
- ciąg
'ca'traktujemy jako listę gdzie na pozycji0jest c, a na pozycji1jest a - warunek
b > 0da nam w wynikutruelubfalse - przemnożenie typu
booleanprzez1jest najprostszym sposobem na uzyskanie wjsz tego typu liczby, co adekwatnie da nam1dlatruei0dlafalse
Rozwiążmy powyższe dla b = 10:
1
2
3
4
5
6
b = 10
// b > 0 => true
// true * 1 = 1
'ca'[( 10 > 0 ) * 1] /* == */ 'ca'[1 * 1] /* == */ 'ca'[1]
// OUTPUT => a, będące na pozycji 1 naszej listy
Można stwierdzić: wszystko pięknie, tylko po co takie udziwnianie, przecież to czyste popisywanie się jak w przypadku
5 * 2 == 5 << 1. W dodatku może być zaprzeczeniem jednej z zasad programowania KISS [Keep it simple stupid], co w wolnym tłumaczeniu znaczy, aby nie udziwniać, czegoś co może być proste.
Oczywiście w powyższym taki kod faktycznie mija się z celem. Najbardziej optymalnym jest ternary, ale czy zawsze tak będzie?
Przykład zastosowania
Otrzymujemy do naszego programu jako dane wejściowe 4 liczby, załóżmy 8, 23, 18, 25, przypiszmy je sobie do zmiennych:
1
2
3
let [a, b, c, d] = [8, 23, 18, 25];
// przy okazji, tak też można
// a = 8, b = 23, c = 18, d = 25
Naszym zadaniem jest napisanie jak najkrótszego kodu, który będzie sprawdzał warunki (poniżej) i w zależności od wyniku poda odpowiednią wartość S, N, E, W, SE, SW, NE i NW. Gwoli ścisłości, nie ja to wymyśliłam. Jest to jedno z rozwiązań zadania Power of Thor na codingames.
I tak:
- jeśli wartość
b>dwypisz literęSi zwiększ wartośćdo1, - jeśli wartość
b<dwypisz literęNi zmniejsz wartośćdo1, - jeśli wartość
a>cwypisz (lub dodaj) literęEi zwiększ wartośćco1, - jeśli wartość
a<cwypisz (lub dodaj) literęWi zmniejsz wartośćco1
W standardowym, nieudziwnionym kodzie mielibyśmy mniej więcej taki zapis:
1
2
3
4
5
6
7
8
9
10
let [a, b, c, d] = [8, 23, 18, 25];
let m = '';
if (b > d) m = 'S', d++;
if (b < d) m = 'N', d--;
if (a > c) m += 'E', c++;
if (a < c) m += 'W', c--;
console.log(m)
// OUTPUT => NW
// bo d = 25 > b = 23 i a = 8 < c = 18
To teraz rozważmy taki zapis powyższego [uwaga, bo będzie trochę komentarzy w kodzie]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let [a, b, c, d] = [8, 23, 18, 25];
let diff_bd = Math.sign(b - d), // 23 - 25 = Math.sign(-2) => -1
diff_ac = Math.sign(a - c); // 8 - 18 = Math.sign(-10) => -1
// Math.sign zwraca wartość:
// -1 dla wartości ujemnych
// 0 dla wartości 0
// 1 dla wartości dodatnich
// a w sumie o taką informację nam głównie chodzi
m = ( 'N S'[diff_bd + 1] + 'W E'[diff_ac + 1] ).trim();
// jeśli diff_bc == -1 na konsoli otrzymamy wartość "N"
// bo -1 + 1 = 0 => 'N S'[0] => "N"
// jeśli diff_bd == 0 w wyniku otrzymamy " "
// bo 0 + 1 = 1 => 'N S'[1] => " ".trim() => '' więc na konsoli nie będzie niczego z tej części
// jeśli diff_bd == 1 na konsoli otrzymamy wartość "S"
// bo 1 + 1 = 2 => 'N S'[2] => "S"
d += diff_bd; // gdzie d+-1 => d--
c += diff_ac; // gdzie c+-1 => c--
console.log(m)
// OUTPUT => NW
Porównajmy oba kody:
1
2
3
4
5
6
7
8
9
10
11
12
13
let [a, b, c, d] = [8, 23, 18, 25],
m = '';
if (b > d) m = 'S', d++;
if (b < d) m = 'N', d--;
if (a > c) m += 'E', c++;
if (a < c) m += 'W', c--;
console.log(m)
// OUTPUT => NW
1
2
3
4
5
6
7
8
9
10
11
12
13
let [a, b, c, d] = [8, 23, 18, 25],
l = Math.sign,
j = l(b - d),
k = l(a - c);
m = ( 'N S'[j + 1] +
'W E'[k + 1] ).trim();
d += j;
c += k;
console.log(m)
// OUTPUT => NW
Nie dość, że po prawej kod wydaje się dłuższy, to w dodatku jest mniej czytelny. Jest jedno ale, wydaje się nam.
Jeśli idzie o ilość znaków użytych do naszego zadania to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
`[a, b, c, d] = [8, 23, 18, 25],
m = '';
if (b > d) m = 'S', d++;
if (b < d) m = 'N', d--;
if (a > c) m += 'E', c++;
if (a < c) m += 'W', c--;
console.log(m)`.
replace(/\r\n|\n|\r|\t|\s/gm,'').length
// da nam 112 znaków
1
2
3
4
5
6
7
8
9
10
11
12
13
14
`[a, b, c, d] = [8, 23, 18, 25],
l = Math.sign,
j = l(b - d),
k = l(a - c);
m = ( 'N S'[j + 1] +
'W E'[k + 1] ).trim();
d += j;
c += k;
console.log(m)`.
replace(/\r\n|\n|\r|\t|\s/gm,'').length
// da nam 108 znaków
Ponadto:
-
Nie powtarzamy 4 razy warunku if, pomimo, że de facto jest on sprawdzany.
Jeśli liczbab > dich różnica będzie ujemna. Ta sama reguła tyczy sięaic. - Zmiennej
lprzypisujemy funkcjęMath.sign, bo użyjemy jej co najmniej dwukrotnie, stąd w naszym kodziel(a-c)il(b-d). - Przypisanie wartości dla zmiennej
mjest tylko raz. - A na koniec w jednej linijce zmieniamy (czyli podwyższamy lub zmniejszamy o 1) wartość zmiennych
cid.
I jak to w życiu, tak w programowaniu sami musimy odpowiedzieć sobie na pytanie. Która strategia jest odpowiednia dla nas i naszego celu ![]()
