• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Dwie tablice, dwie pętle i zasieg: lokalny i globalny - jak dziala ten kod?

+4 głosów
107 wizyt
pytanie zadane 4 marca 2021 w JavaScript przez lukasz21 Obywatel (1,100 p.)
zamknięte 11 kwietnia 2021 przez lukasz21


 

Witam,

 

Trafiłem na 2 przykłady kodu JS które nie rozumiem jak działają. Może mi ktoś wytłumaczyć mi jak to działa? Są to dwie tablice, dwie pętle i zasieg.

W 1 wersji jest zasieg globalny, tablica ROW jest na zewnątrz pętli FOR 1 lvl. W 2 wersji jest zasięg lokalny, tablica ROW jest wewnątrz pętli FOR 1 lvl.

 

Od razy uprzedzam, że kod dla tych przykładów jest złożony i długi.  Starałem się moimi słowami krok po kroku opisać jak powinien on według mnie działać, tak aby to było dobrze zrozumiałe dla osób to czytających. Jeśli coś napisałem niejasno to napiszcie, to spróbuje to lepiej wytłumaczyć.

Z góry dziekuję za pomoc.

 

Budowa: najpierw podaje kod, potem opisuje co kod robi i na końcu jaki jest problem.

 

Wersja 1

 

function zeroArray(m, n) {

      let newArray = [];
      let row = []; // zasieg globalny

      for (let i = 0; i < m; i++) {

        for (let j = 0; j < n; j++) {
          row.push(0);
        }
        newArray.push(row);
        console.log(newArray)
      }
      return newArray;
    }

    let matrix = zeroArray(3, 2);
    console.log(matrix);


Opis:

 

Funkcja ma 2 parametry: ‘m’ dla 1 lvl for i ‘n’ dla 2 lvl for.

Według mojego zrozumienia kod powinien działać tak:

idzie od góry, jest 1 obrót pętli FOR 1 lvl.

W niej petla 2 lvl do tablicy ‘row’ dodaje dwa razy ‘0’, więc teraz jest w ‘row’ [0,0].

Dalej dodaje do tablicy ‘newArray’ treść z ‘row’, czyli w ‘newArray’ jest teraz: [ [0,0] ].

koniec 1 obrotu pętli FOR 1 lvl.

 

Nastepuje 2 obórt petli FOR 1 lvl.

UWAGA: Tablica ‘row’ ma zasieg globalny więc po 1 oborcie petli FOR 1 lvl ta tablica ma już tresc i jest to: [0,0].

Petla FOR 2 lvl dodaje do niej dwa razy ‘0’, więc ‘row’ ma teraz wartosc [0,0 , 0,0].

Dalej dodaje do tablicy ‘newArray’ treść z ‘row’. Po 1 obrocie petli FOR 1 lvl w ‘newArray’ jest [0,0]. W tym obrocie ‘row’ ma wartosc [0,0 , 0,0]. Dlatego: teraz będzie: [ [0,0] ,[0,0 , 0,0] ].

koniec 2 obrotu pętli FOR 1 lvl.

 

Nastepuje 3 obórt petli FOR 1 lvl.

UWAGA: Tablica ‘row’ ma zasieg globalny więc po 2 oborcie petli FOR 1 lvl ta tablica ma już tresc i jest to: [0,0,0,0].

Petla FOR 2 lvl dodaje do niej dwa razy ‘0’, więc ‘row’ ma teraz wartosc [0,0 , 0,0 , 0 ,0 ].

Dalej dodaje do tablicy ‘newArray’ treść z ‘row’. Po 2 obrocie petli FOR 1 lvl w ‘newArray’ jest [ [0,0] ,[0,0 , 0,0] ].

W tym obrocie ‘row’ ma wartosc [0,0 , 0,0 , 0,0]. Dlatego: teraz będzie: [ [0,0] ,[0,0 , 0,0] , [0,0 , 0,0 ,0 , 0] ].

koniec 3 obrotu pętli FOR 1 lvl

 

Na koniec funkcji dla ‘return newArray’ zwraca: [ [0,0 , 0,0 ,0,0] , [0,0 , 0,0 ,0,0] , [0,0, 0,0  ,0,0] ].


Problem:

Moje pytanie: czemu zwraca taki wynik, skoro według mnie powinno być:

[ [0,0] ,[0,0 , 0,0] , [0,0 , 0,0 ,0,0]] ?
 

 

wersja 2

 

    function zeroArray2(m, n) {

      let newArray = [];
      for (let i = 0; i < m; i++) {

        let row = [];	// zasieg lokalny
 
        for (let j = 0; j < n; j++) {
          row.push(0);
        }
        newArray.push(row);
        console.log(newArray)

      }
      return newArray;
    }

    let matrix2 = zeroArray2(3, 2);
    console.log(matrix2);

 

Opis

 

Funkcja ma 2 parametry: ‘m’ dla 1 lvl for i ‘n’ dla 2 lvl for.

Według mojego zrozumienia kod powinien działać tak:

idzie od góry, jest 1 obrót pętli FOR 1 lvl.

W niej petla 2 lvl do tablicy ‘row’ dodaje dwa razy ‘0’, więc teraz jest w ‘row’ [0,0].

Dalej dodaje do tablicy ‘newArray’ treść z ‘row’, czyli w ‘newArray’ jest teraz: [ [0,0] ].

koniec 1 obrotu pętli FOR 1 lvl.

 

Nastepuje 2 obórt petli FOR 1 lvl.

Tablica ‘row’ jest wyczyszczona (bo tablica jest zmienna lokalna), więc jest pusta. Petla FOR 2 lvl dodaje do niej dwa razy ‘0’, więc ‘row’ ma teraz wartosc [0,0].

Dalej dodaje do tablicy ‘newArray’ treść z ‘row’. Po 1 obrocie petli FOR 1 lvl w ‘newArray’ jest [0,0], więc teraz będzie: [ [0,0] ,[0,0] ].

koniec 2 obrotu pętli FOR 1 lvl.

 

Nastepuje 3 obórt petli FOR 1 lvl.

Tablica ‘row’ jest wyczyszczona, więc jest pusta. Petla FOR 2 lvl dodaje do niej dwa razy ‘0’, więc ‘row’ ma teraz wartosc [ [0,0] ].

Dalej dodaje do tablicy ‘newArray’ treść z ‘row’. Po 2 obrocie petli FOR 1 lvl w ‘newArray’ jest [ [0,0] ,[0,0] ], więc teraz będzie: [ [0,0] ,[0,0] ,[0,0] ].

koniec 3 obrotu pętli FOR 1 lvl
 

Na koniec funkcji dla ‘return newArray’ zwraca: [ [0,0] ,[0,0] ,[0,0] ].

 

Problem:

 

Kod działa zgodnie z założeniem, ale: dla każdego obrotu pętli FOR 1 lvl (poziomu) ‘console.log(newArray)’ pokazuje w konsoli taka tresc jaka ta tablica powinna mieć na koniec wykonania kodu, tak gdzie jest ‘return newArray’.

Powinno to pokazywac:

[ [0,0] ].

[ [0,0] ,[0,0] ].

[ [0,0] ,[0,0] ,[0,0] ].

Zamiast tego pokazuje 3 razy ta treść:

[ [0,0] ,[0,0] ,[0,0] ].

Czemu tak jest? Jakby wypisywało przy każdym ‘console.log’ wartość jaka tablica będzie mieć na końcu wykonania kodu.

 


 

Codepen dodatkowo

https://codepen.io/luk21/pen/RwoBZyL


 

 

 

 

komentarz zamknięcia: Rozwiazałem problem
komentarz 4 marca 2021 przez ScriptyChris Mędrzec (171,780 p.)
Kurczę, fajnie by było jakby wszystkie pytania na forum były choćby w połowie tak szczegółowo opisane jak to. ;)
1
komentarz 5 marca 2021 przez VBService VIP (144,910 p.)
edycja 5 marca 2021 przez VBService

@lukasz21,   wink

to

function zeroArray2(m, n) {
  let newArray = [];
  for (let i = 0; i < m; i++) {
    let row = [];    // zasieg lokalny
    for (let j = 0; j < n; j++) {
      row.push(0);
    }
    newArray.push(row);
    console.log(newArray);
  }
  return newArray;
}

let matrix2 = zeroArray2(3, 2);
console.log(matrix2);

na

function zeroArray2(m, n, f=0) {
  return Array.from(Array(m), () => new Array(n).fill(f)); // wariant 1
  // return Array.from(Array(m), () => new Array(n).fill(f)); // wariant 2
  // return Array.from(Array(m), () => new Array(n).fill(f++)); // wariant 3  
}

console.log(zeroArray2(3, 2)); // wariant 1
// console.log(zeroArray2(3, 2, 1)); // wariant 2
// console.log(zeroArray2(3, 2, 1)); // wariant 3

 

komentarz 5 marca 2021 przez lukasz21 Obywatel (1,100 p.)
Dzięki, warta poznania struktura.

1 odpowiedź

+2 głosów
odpowiedź 5 marca 2021 przez ScriptyChris Mędrzec (171,780 p.)
wybrane 11 kwietnia 2021 przez lukasz21
 
Najlepsza

1. Zmienna row nie ma zasięgu globalnego, tylko lokalny. Zasięg globalny mają zmienne deklarowane niejawnie lub utworzone bezpośrednio w scopie globalnym - czyli poza funkcją/metodą, blokiem (dotyczy użycia słówek let i const), w niektórych przypadkach użycia eval oraz w przypadku identyfikatora dla wyjątku przekazanego do catch w konstrukcji try...catch.

Problem:

Moje pytanie: czemu zwraca taki wynik, skoro według mnie powinno być:

[ [0,0] ,[0,0 , 0,0] , [0,0 , 0,0 ,0,0]] ?

Gdy pod koniec każdej iteracji zewnętrznej (pierwszej) pętli tablica row jest pushowana do tablicy newArray, to jest to ciągle ta sama referencja. Więc w każdej iteracji wewnętrznej (drugiej) pętli, pushowanie do row kolejnych zer dodaje je również do tych samych odpowiedników tablicy row w tablicy newArray.

Efekt, którego się spodziewasz, można osiągnąć po dwóch modyfikacjach kodu:

  • tablica row powinna być zadeklarowana w pierwszej pętli
  • warunek zakończenia drugiej pętli powinien być j < n * (i + 1), wtedy każda iteracja wewnętrznej pętli będzie i + 1 krotnie dłuższa od wartości w przekazanym parametrze n. Raczej samo n * i by wystarczyło, gdyby nie fakt, że zewnętrzna pętla iteruje od zera, a nie od np. 1 (bo mnożenie przez 0 daje 0). I możliwe, że pod względem algorytmu można to zapisać lepiej:
    function zeroArray(m, n) {
       let newArray = [];
          
     
      for (let i = 0; i < m; i++) {
        let row = [];
    
        for (let j = 0; j < n * (i + 1); j++) {
          row.push(0);
        }
    
        newArray.push(row);
      }
      return newArray;
    }
    
    console.log(zeroArray(3, 2));
    /*
      0: (2) [0, 0]
      1: (4) [0, 0, 0, 0]
      2: (6) [0, 0, 0, 0, 0, 0]
    */

     

2. console.log pokazuje zawartość tablicy w czasie wykonania kodu. Natomiast jeśli jest to obiekt (a tablica jest obiektem), to możesz go rozwinąć, żeby zobaczyć co zawiera (elementy indeksowanej listy tablicy lub propertisy obiektu). W tym momencie konsola odświeży pokazaną zawartość do aktualnego stanu (pokaże się wtedy ikonka informująca o tym), czyli już po zakończeniu iteracji pętli. Innymi słowy, rozwinięcie obiektu wylogowanego do konsoli pokazuje jego aktualny stan.

Jeśli jednak chcesz, aby konsola zawsze pokazała stan obiektu w momencie logowania, bez względu na to kiedy rozwiniesz sobie zawartość, to loguj sklonowany obiekt przy pomocy np. kombinacji JSON.parse(JSON.stringify( obiekt )), albo [ ...tablica ], albo { ...obiekt } - dwa ostatnie korzystają z tzw. shallow copy przy pomocy spread operatora.

komentarz 5 marca 2021 przez lukasz21 Obywatel (1,100 p.)
Dziękuję za odpowiedź. Muszę to jeszcze przemyśleć. Będę miał pytanie to napisze.

Podobne pytania

0 głosów
0 odpowiedzi 130 wizyt
0 głosów
2 odpowiedzi 341 wizyt
pytanie zadane 10 stycznia 2018 w C i C++ przez Sansi Użytkownik (720 p.)
+2 głosów
0 odpowiedzi 98 wizyt

86,485 zapytań

135,241 odpowiedzi

300,487 komentarzy

57,234 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...