• 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?

VPS Starter Arubacloud
+3 głosów
222 wizyt
pytanie zadane 4 marca 2021 w JavaScript przez lukasz21 Obywatel (1,090 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 (190,190 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 Ekspert (251,170 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,090 p.)
Dzięki, warta poznania struktura.

1 odpowiedź

+2 głosów
odpowiedź 5 marca 2021 przez ScriptyChris Mędrzec (190,190 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,090 p.)
Dziękuję za odpowiedź. Muszę to jeszcze przemyśleć. Będę miał pytanie to napisze.

Podobne pytania

0 głosów
0 odpowiedzi 174 wizyt
0 głosów
2 odpowiedzi 404 wizyt
pytanie zadane 10 stycznia 2018 w C i C++ przez Sansi Użytkownik (720 p.)
+2 głosów
0 odpowiedzi 279 wizyt
pytanie zadane 28 lipca 2021 w JavaScript przez lucasio99 Początkujący (320 p.)

92,451 zapytań

141,261 odpowiedzi

319,073 komentarzy

61,853 pasjonatów

Motyw:

Akcja Pajacyk

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

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...