Бит опоздал на вечеринку, но я изучал этот вопрос сегодня и заметил, что многие ответы не полностью касаются того, как Javascript обрабатывает области, что по существу то, что это сводится к.
So как упоминалось многими другими, проблема в том, что внутренняя функция ссылается на одну и ту же переменную i
. Итак, почему бы нам просто не создать новую локальную переменную на каждой итерации и вместо этого иметь ссылку на внутреннюю функцию?
//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '' + msg + '
';};
var funcs = {};
for (var i = 0; i < 3; i++) {
var ilocal = i; //create a new local variable
funcs[i] = function() {
console.log("My value: " + ilocal); //each should reference its own local variable
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Точно так же раньше, когда каждая внутренняя функция выдавала последнее значение, присвоенное i
, теперь каждая внутренняя функция просто выводит последнее значение, назначенное на ilocal
. Но не должна ли каждая итерация иметь свою собственную ilocal
?
Оказывается, в этом и проблема. Каждая итерация разделяет одну и ту же область, поэтому каждая итерация после первого просто переписывается ilocal
. Из MDN :
Важно: JavaScript не имеет области блока. Переменные, введенные с блоком, привязаны к содержащейся функции или скрипту, а эффекты их настройки сохраняются за пределами самого блока. Другими словами, операторы блоков не вводят область. Хотя «автономные» блоки являются допустимым синтаксисом, вы не хотите использовать автономные блоки в JavaScript, потому что они не делают то, что, по вашему мнению, они делают, если вы думаете, что они делают что-то вроде таких блоков на C или Java.
Повторяется для акцента:
JavaScript не имеет области блока. Переменные, введенные с блоком, привязаны к содержащейся функции или скрипту
. Мы можем увидеть это, проверив
ilocal
, прежде чем объявить его на каждой итерации:
//overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '
' + msg + '
';}; var funcs = {}; for (var i = 0; i < 3; i++) { console.log(ilocal); var ilocal = i; }Именно поэтому эта ошибка настолько сложна. Несмотря на то, что вы обновляете переменную, Javascript не будет вызывать ошибку, и JSLint даже не выдаст предупреждение. Именно поэтому лучший способ решить эту проблему - воспользоваться преимуществами закрытия, что по сути является идеей, что в Javascript внутренние функции имеют доступ к внешним переменным, потому что внутренние области «заключают» внешние области.
[/g13]
Это также означает, что внутренние функции «удерживают» внешние переменные и сохраняют их, даже если внешняя функция возвращается. Чтобы использовать это, мы создаем и вызываем функцию-оболочку только для создания новой области, объявляем
ilocal
в новой области и возвращаем внутреннюю функцию, которая используетilocal
(более подробное объяснение ниже):
//overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '
' + msg + '
';}; var funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = (function() { //create a new scope using a wrapper function var ilocal = i; //capture i into a local var return function() { //return the inner function console.log("My value: " + ilocal); }; })(); //remember to run the wrapper function } for (var j = 0; j < 3; j++) { funcs[j](); }Создание внутренней функции внутри функции-обертки дает внутренней функции частную среду, доступ к которой может получить только «закрытие». Таким образом, каждый раз, когда мы вызываем функцию-оболочку, мы создаем новую внутреннюю функцию с ее собственной отдельной средой, гарантируя, что переменные
ilocal
не сталкиваются и не перезаписывают друг друга. Несколько незначительных оптимизаций дают окончательный ответ, который дали многие другие пользователи SO:
//overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '
' + msg + '
';}; var funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = wrapper(i); } for (var j = 0; j < 3; j++) { funcs[j](); } //creates a separate environment for the inner function function wrapper(ilocal) { return function() { //return the inner function console.log("My value: " + ilocal); }; }Обновить
С ES6 теперь mainstream, теперь мы можем использовать новое ключевое слово
let
для создания переменных с блочным диапазоном:
//overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '
' + msg + '
';}; var funcs = {}; for (let i = 0; i < 3; i++) { // use "let" to declare "i" funcs[i] = function() { console.log("My value: " + i); //each should reference its own local variable }; } for (var j = 0; j < 3; j++) { // we can use "var" here without issue funcs[j](); }Посмотрите, как легко это сейчас! Для получения дополнительной информации см. этот ответ , на котором основана моя информация.
Для коротких массивов использование наборов, вероятно, является самым ясным и наиболее читаемым способом его выполнения.
Другой способ - использовать numpy.intersect1d
. Вы должны будете обмануть его для обработки строк как одного значения, хотя ... Это делает вещи немного менее читаемыми ...
import numpy as np
A = np.array([[1,4],[2,5],[3,6]])
B = np.array([[1,4],[3,6],[7,8]])
nrows, ncols = A.shape
dtype={'names':['f{}'.format(i) for i in range(ncols)],
'formats':ncols * [A.dtype]}
C = np.intersect1d(A.view(dtype), B.view(dtype))
# This last bit is optional if you're okay with "C" being a structured array...
C = C.view(A.dtype).reshape(-1, ncols)
Для больших массивов это должно быть значительно быстрее, чем используя наборы.
np.array(set(map(tuple, b)).difference(set(map(tuple, a))))
Это также может работать
Вы можете использовать наборы Python:
>>> import numpy as np
>>> A = np.array([[1,4],[2,5],[3,6]])
>>> B = np.array([[1,4],[3,6],[7,8]])
>>> aset = set([tuple(x) for x in A])
>>> bset = set([tuple(x) for x in B])
>>> np.array([x for x in aset & bset])
array([[1, 4],
[3, 6]])
Как указывает Роб Коуи, это можно сделать более кратко, так как
np.array([x for x in set(tuple(x) for x in A) & set(tuple(x) for x in B)])
Возможно, есть способ сделать это без все идет назад и вперед от массивов к кортежам, но это не приходит ко мне прямо сейчас.
Другой способ добиться этого с помощью структурированного массива:
>>> a = np.array([[3, 1, 2], [5, 8, 9], [7, 4, 3]])
>>> b = np.array([[2, 3, 0], [3, 1, 2], [7, 4, 3]])
>>> av = a.view([('', a.dtype)] * a.shape[1]).ravel()
>>> bv = b.view([('', b.dtype)] * b.shape[1]).ravel()
>>> np.intersect1d(av, bv).view(a.dtype).reshape(-1, a.shape[1])
array([[3, 1, 2],
[7, 4, 3]])
Просто для ясности структурированный вид выглядит следующим образом:
>>> a.view([('', a.dtype)] * a.shape[1])
array([[(3, 1, 2)],
[(5, 8, 9)],
[(7, 4, 3)]],
dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8')])
Я не мог понять, почему нет предлагаемого чистого numpy способа заставить это работать. Поэтому я нашел один, который использует широковещательную передачу numpy. Основная идея - преобразовать один из массивов в 3d путем замены осей. Давайте построим 2 массива:
a=np.random.randint(10, size=(5, 3))
b=np.zeros_like(a)
b[:4,:]=a[np.random.randint(a.shape[0], size=4), :]
С моим запуском он дал:
a=array([[5, 6, 3],
[8, 1, 0],
[2, 1, 4],
[8, 0, 6],
[6, 7, 6]])
b=array([[2, 1, 4],
[2, 1, 4],
[6, 7, 6],
[5, 6, 3],
[0, 0, 0]])
Шаги (массивы могут быть взаимозаменяемы):
#a is nxm and b is kxm
c = np.swapaxes(a[:,:,None],1,2)==b #transform a to nx1xm
# c has nxkxm dimensions due to comparison broadcast
# each nxixj slice holds comparison matrix between a[j,:] and b[i,:]
# Decrease dimension to nxk with product:
c = np.prod(c,axis=2)
#To get around duplicates://
# Calculate cumulative sum in k-th dimension
c= c*np.cumsum(c,axis=0)
# compare with 1, so that to get only one 'True' statement by row
c=c==1
#//
# sum in k-th dimension, so that a nx1 vector is produced
c=np.sum(c,axis=1).astype(bool)
# The intersection between a and b is a[c]
result=a[c]
В функции с 2 строками для уменьшения используемой памяти (исправьте меня, если не так):
def array_row_intersection(a,b):
tmp=np.prod(np.swapaxes(a[:,:,None],1,2)==b,axis=2)
return a[np.sum(np.cumsum(tmp,axis=0)*tmp==1,axis=1).astype(bool)]
, который дал результат для моего примера:
result=array([[5, 6, 3],
[2, 1, 4],
[6, 7, 6]])
Это быстрее, чем установлено решений, поскольку он использует только простые операции numpy, в то время как он уменьшает постоянство размеров и идеально подходит для двух больших матриц. Наверное, я мог ошибаться в своих комментариях, так как получил ответ от экспериментов и инстинкта. Эквивалент для пересечения столбцов можно найти либо путем переноса массивов, либо путем небольшого изменения шагов. Кроме того, если нужны дубликаты, тогда шаги внутри «//» должны быть пропущены. Функция может быть отредактирована, чтобы возвращать только булевский массив индексов, который мне пригодился, пытаясь получить разные индексы массивов с одним и тем же вектором. Контрольный показатель для проголосовавшего ответа и моего (количество элементов в каждом измерении играет роль в выборе):
Код:
def voted_answer(A,B):
nrows, ncols = A.shape
dtype={'names':['f{}'.format(i) for i in range(ncols)],
'formats':ncols * [A.dtype]}
C = np.intersect1d(A.view(dtype), B.view(dtype))
return C.view(A.dtype).reshape(-1, ncols)
a_small=np.random.randint(10, size=(10, 10))
b_small=np.zeros_like(a_small)
b_small=a_small[np.random.randint(a_small.shape[0],size=[a_small.shape[0]]),:]
a_big_row=np.random.randint(10, size=(10, 1000))
b_big_row=a_big_row[np.random.randint(a_big_row.shape[0],size=[a_big_row.shape[0]]),:]
a_big_col=np.random.randint(10, size=(1000, 10))
b_big_col=a_big_col[np.random.randint(a_big_col.shape[0],size=[a_big_col.shape[0]]),:]
a_big_all=np.random.randint(10, size=(100,100))
b_big_all=a_big_all[np.random.randint(a_big_all.shape[0],size=[a_big_all.shape[0]]),:]
print 'Small arrays:'
print '\t Voted answer:',timeit.timeit(lambda:voted_answer(a_small,b_small),number=100)/100
print '\t Proposed answer:',timeit.timeit(lambda:array_row_intersection(a_small,b_small),number=100)/100
print 'Big column arrays:'
print '\t Voted answer:',timeit.timeit(lambda:voted_answer(a_big_col,b_big_col),number=100)/100
print '\t Proposed answer:',timeit.timeit(lambda:array_row_intersection(a_big_col,b_big_col),number=100)/100
print 'Big row arrays:'
print '\t Voted answer:',timeit.timeit(lambda:voted_answer(a_big_row,b_big_row),number=100)/100
print '\t Proposed answer:',timeit.timeit(lambda:array_row_intersection(a_big_row,b_big_row),number=100)/100
print 'Big arrays:'
print '\t Voted answer:',timeit.timeit(lambda:voted_answer(a_big_all,b_big_all),number=100)/100
print '\t Proposed answer:',timeit.timeit(lambda:array_row_intersection(a_big_all,b_big_all),number=100)/100
с результатами:
Small arrays:
Voted answer: 7.47108459473e-05
Proposed answer: 2.47001647949e-05
Big column arrays:
Voted answer: 0.00198730945587
Proposed answer: 0.0560171294212
Big row arrays:
Voted answer: 0.00500325918198
Proposed answer: 0.000308241844177
Big arrays:
Voted answer: 0.000864889621735
Proposed answer: 0.00257176160812
После вынесения вердикта следует, что если вам нужно сравнить 2 больших 2d массива из 2d точек, используйте голосовой ответ. Если у вас большие матрицы во всех измерениях, проголосовавший ответ - лучший из всех. Таким образом, это зависит от того, что вы выбираете каждый раз.