JS, разница в матрице массивов и поведение forEach - PullRequest
0 голосов
/ 12 октября 2018

Я выполнял некоторые учебные задания для своего курса JS, и у меня есть такая, где вы должны реализовать функцию, которая принимает положительное целое число (n) и возвращает матрицу, подобную приведенной ниже (5 было пройдено):

[ [ 1, 0, 0, 0, 0 ],
  [ 0, 1, 0, 0, 0 ],
  [ 0, 0, 1, 0, 0 ],
  [ 0, 0, 0, 1, 0 ],
  [ 0, 0, 0, 0, 1 ] ]

Мне удалось реализовать функцию с помощью следующего кода:

function getIdentityMatrix(n) {
  const mat = new Array(n).fill([]);
  return mat.map((row, index) => {
    row = new Array(n).fill(0);
    row[index] = 1;
    return row;
  });
}

Но при этом я обнаружил странное поведение, которое не могу объяснить ... Если я немного изменю код:

function getIdentityMatrix(n) {
  const mat = new Array(n).fill(new Array(n).fill(0));
  return mat.map((row, index) => {
    row[index] = 1;
    return row;
  });
}

Возвращает матрицу, подобную этой:

[ [ 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1 ] ]

Почему это так работает?Это похоже на то, что функция forEach выполняет итерацию по всем элементам, вложенным в каждую строку, чего не следует делать.

Спасибо за любые советы!

Ответы [ 3 ]

0 голосов
/ 12 октября 2018
// your example is roughly equivalent to this.
const innerArray = new Array(n).fill(0);
const mat = new Array(n).fill(innerArray);

(mat[0] === mat[1] === innerArray) === true;

есть только 1 вложенный массив, а не массив n раз.

0 голосов
/ 13 октября 2018

Код, о котором идет речь, эквивалентен этому:

let n = 5
let innerArr = new Array(n).fill(0)

function getIdentityMatrix(n) {
  const mat = new Array(n).fill(innerArr);
  return mat.map((row, index) => {
    row[index] = 1;
    return row;
  });
}
console.log(getIdentityMatrix(n))

Поскольку вы используете fill, вы в основном заполняете этот массив mat ссылками на innerArr (which you can see clearly from the above console output).

Тогда высделайте row[index] = 1 для каждого i , который изменяет одинаковые значения (по индексу i) того же массива .

Теперь ваш рабочий пример ... который можетзаписать в более короткой форме как:

const getIdentityMatrix = (n) =>
   [...Array(n)].map((row, index) => {
     row = Array(n).fill(0)
     row[index] = 1
     return row
   })

console.log(getIdentityMatrix(3))

Ясно maps поверх вновь созданного, а затем spreaded массива n, но затем перезаписывает каждый элемент с совершенно новой ссылкой на массив .

Поскольку эта ссылка является новой, ее изменение с помощью row[index] = 1 создает ожидаемое поведение, когда мы return the x из карты.

Еще один способ добиться этого в одной строке - через map, Object.assign и Object.values ​​ следующим образом:

const gm = (n) => [...Array(n)].map((x,i) => 
   Object.values(Object.assign(Object.assign({}, Array(n).fill(0)), {[i]:1})))

console.log(gm(3))
0 голосов
/ 12 октября 2018

Это потому, что Array является ссылочным типом.Когда вы делаете

new Array(n).fill(new Array(n).fill(0))

сначала, внутренний new Array(n).fill(0) создает размер массива n, заполненный 0;затем внешний Array(n).fill создает массив, заполненный n ссылками на этот внутренний массив.

Он не создает n внутренних массивов, просто n ссылок на один и тот же массив.Поэтому, когда вы изменяете элемент этого внутреннего массива, все ссылки во внешнем массиве будут отражать это изменение, поскольку все они указывают на один и тот же объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...