Бит опоздал на вечеринку, но я изучал этот вопрос сегодня и заметил, что многие ответы не полностью касаются того, как 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](); }Посмотрите, как легко это сейчас! Для получения дополнительной информации см. этот ответ , на котором основана моя информация.
Я прочитал работу Уно и попытался придумать реализацию. Ниже приведен мой очень длинный код с рабочим примером. В этом конкретном случае существует 4 «допустимых» вершины (по терминологии Uno), поэтому, переключая каждую с уже покрытой вершиной, вы все вместе 2^4 = 16
допускаете различные возможные максимальные соответствия.
Я должен признаю, что я очень новичок в теории графов, и я не следил за процессами Uno точно, есть незначительные различия и в основном я не пытался делать какие-либо оптимизации. Я действительно пытался понять документ, поскольку, по-моему, объяснения не совсем совершенны, и цифры могут иметь ошибки в них. Поэтому, пожалуйста, используйте с осторожностью, и если вы можете помочь оптимизировать его, это будет здорово!
import networkx as nx
from networkx import bipartite
def plotGraph(graph):
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(111)
pos=[(ii[1],ii[0]) for ii in graph.nodes()]
pos_dict=dict(zip(graph.nodes(),pos))
nx.draw(graph,pos=pos_dict,ax=ax,with_labels=True)
plt.show(block=False)
return
def formDirected(g,match):
'''Form directed graph D from G and matching M.
<g>: undirected bipartite graph. Nodes are separated by their
'bipartite' attribute.
<match>: list of edges forming a matching of <g>.
Return <d>: directed graph, with edges in <match> pointing from set-0
(bipartite attribute ==0) to set-1 (bipartite attrbiute==1),
and the other edges in <g> but not in <matching> pointing
from set-1 to set-0.
'''
d=nx.DiGraph()
for ee in g.edges():
if ee in match or (ee[1],ee[0]) in match:
if g.node[ee[0]]['bipartite']==0:
d.add_edge(ee[0],ee[1])
else:
d.add_edge(ee[1],ee[0])
else:
if g.node[ee[0]]['bipartite']==0:
d.add_edge(ee[1],ee[0])
else:
d.add_edge(ee[0],ee[1])
return d
def enumMaximumMatching(g):
'''Find all maximum matchings in an undirected bipartite graph.
<g>: undirected bipartite graph. Nodes are separated by their
'bipartite' attribute.
Return <all_matches>: list, each is a list of edges forming a maximum
matching of <g>.
'''
all_matches=[]
#----------------Find one matching M----------------
match=bipartite.hopcroft_karp_matching(g)
#---------------Re-orient match arcs---------------
match2=[]
for kk,vv in match.items():
if g.node[kk]['bipartite']==0:
match2.append((kk,vv))
match=match2
all_matches.append(match)
#-----------------Enter recursion-----------------
all_matches=enumMaximumMatchingIter(g,match,all_matches,None)
return all_matches
def enumMaximumMatchingIter(g,match,all_matches,add_e=None):
'''Recurively search maximum matchings.
<g>: undirected bipartite graph. Nodes are separated by their
'bipartite' attribute.
<match>: list of edges forming one maximum matching of <g>.
<all_matches>: list, each is a list of edges forming a maximum
matching of <g>. Newly found matchings will be appended
into this list.
<add_e>: tuple, the edge used to form subproblems. If not None,
will be added to each newly found matchings.
Return <all_matches>: updated list of all maximum matchings.
'''
#---------------Form directed graph D---------------
d=formDirected(g,match)
#-----------------Find cycles in D-----------------
cycles=list(nx.simple_cycles(d))
if len(cycles)==0:
#---------If no cycle, find a feasible path---------
all_uncovered=set(g.node).difference(set([ii[0] for ii in match]))
all_uncovered=all_uncovered.difference(set([ii[1] for ii in match]))
all_uncovered=list(all_uncovered)
#--------------If no path, terminiate--------------
if len(all_uncovered)==0:
return all_matches
#----------Find a length 2 feasible path----------
idx=0
uncovered=all_uncovered[idx]
while True:
if uncovered not in nx.isolates(g):
paths=nx.single_source_shortest_path(d,uncovered,cutoff=2)
len2paths=[vv for kk,vv in paths.items() if len(vv)==3]
if len(len2paths)>0:
reversed=False
break
#----------------Try reversed path----------------
paths_rev=nx.single_source_shortest_path(d.reverse(),uncovered,cutoff=2)
len2paths=[vv for kk,vv in paths_rev.items() if len(vv)==3]
if len(len2paths)>0:
reversed=True
break
idx+=1
if idx>len(all_uncovered)-1:
return all_matches
uncovered=all_uncovered[idx]
#-------------Create a new matching M'-------------
len2path=len2paths[0]
if reversed:
len2path=len2path[::-1]
len2path=zip(len2path[:-1],len2path[1:])
new_match=[]
for ee in d.edges():
if ee in len2path:
if g.node[ee[1]]['bipartite']==0:
new_match.append((ee[1],ee[0]))
else:
if g.node[ee[0]]['bipartite']==0:
new_match.append(ee)
if add_e is not None:
for ii in add_e:
new_match.append(ii)
all_matches.append(new_match)
#---------------------Select e---------------------
e=set(len2path).difference(set(match))
e=list(e)[0]
#-----------------Form subproblems-----------------
g_plus=g.copy()
g_minus=g.copy()
g_plus.remove_node(e[0])
g_plus.remove_node(e[1])
g_minus.remove_edge(e[0],e[1])
add_e_new=[e,]
if add_e is not None:
add_e_new.extend(add_e)
all_matches=enumMaximumMatchingIter(g_minus,match,all_matches,add_e)
all_matches=enumMaximumMatchingIter(g_plus,new_match,all_matches,add_e_new)
else:
#----------------Find a cycle in D----------------
cycle=cycles[0]
cycle.append(cycle[0])
cycle=zip(cycle[:-1],cycle[1:])
#-------------Create a new matching M'-------------
new_match=[]
for ee in d.edges():
if ee in cycle:
if g.node[ee[1]]['bipartite']==0:
new_match.append((ee[1],ee[0]))
else:
if g.node[ee[0]]['bipartite']==0:
new_match.append(ee)
if add_e is not None:
for ii in add_e:
new_match.append(ii)
all_matches.append(new_match)
#-----------------Choose an edge E-----------------
e=set(match).intersection(set(cycle))
e=list(e)[0]
#-----------------Form subproblems-----------------
g_plus=g.copy()
g_minus=g.copy()
g_plus.remove_node(e[0])
g_plus.remove_node(e[1])
g_minus.remove_edge(e[0],e[1])
add_e_new=[e,]
if add_e is not None:
add_e_new.extend(add_e)
all_matches=enumMaximumMatchingIter(g_plus,match,all_matches,add_e_new)
all_matches=enumMaximumMatchingIter(g_minus,new_match,all_matches,add_e)
return all_matches
if __name__=='__main__':
g=nx.Graph()
edges=[
[(1,0), (0,0)],
[(1,1), (0,0)],
[(1,2), (0,2)],
[(1,3), (0,2)],
[(1,4), (0,3)],
[(1,4), (0,5)],
[(1,5), (0,2)],
[(1,5), (0,4)],
[(1,6), (0,1)],
[(1,6), (0,4)],
[(1,6), (0,6)]
]
for ii in edges:
g.add_node(ii[0],bipartite=0)
g.add_node(ii[1],bipartite=1)
g.add_edges_from(edges)
plotGraph(g)
all_matches=enumMaximumMatching(g)
for mm in all_matches:
g_match=nx.Graph()
for ii in mm:
g_match.add_edge(ii[0],ii[1])
plotGraph(g_match)
В статье «Алгоритмы для перечисления всех совершенных, максимальных и максимальных совпадений в двудольных графах» от Takeaki Uno имеется алгоритм. http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.107.8179&rep=rep1&type=pdf
В теореме 2 сказано: «Максимум сопоставления в двудольном графе могут быть перечислены в O (mn ^ 1/2 + nNm) времени и O (m) пространстве, где Nm - число максимальных совпадений в G. "