어휘 폐쇄는 어떻게 작동합니까?
Javascript 코드에서 어휘 폐쇄와 관련된 문제를 조사하던 중 Python에서 다음 문제를 발견했습니다.
flist = []
for i in xrange(3):
def func(x): return x * i
flist.append(func)
for f in flist:
print f(2)
이 예제는 주의 깊게 방지합니다.lambda
"444"를합니다."444" 를인하는데쇄다, 일은놀니입운라이것▁it▁"다44."024"를 예상합니다.
이와 동등한 Perl 코드로 올바르게 작동합니다.
my @flist = ();
foreach my $i (0 .. 2)
{
push(@flist, sub {$i * $_[0]});
}
foreach my $f (@flist)
{
print $f->(2), "\n";
}
"02 4"가 인쇄됩니다.
차이점을 설명해 주시겠습니까?
업데이트:
문제는 에 있지 않습니다.i
세계적인 것.그러면 동일한 동작이 표시됩니다.
flist = []
def outer():
for i in xrange(3):
def inner(x): return x * i
flist.append(inner)
outer()
#~ print i # commented because it causes an error
for f in flist:
print f(2)
에 있는 주이달선알수있이듯서에린석,▁shows이▁as,듯있▁commented수▁the.i
그 시점에서 알 수 없습니다.그래도 "444"는 인쇄합니다.
Python은 실제로 정의된 대로 작동합니다.세 개의 개별 함수가 생성되지만 각각 정의된 환경(이 경우 글로벌 환경(또는 루프가 다른 함수 내부에 배치된 경우 외부 함수의 환경)의 폐쇄가 있습니다.하지만 이것이 바로 문제입니다. 이 환경에서 i는 수정되고, 폐쇄는 모두 동일한 i를 가리킵니다.
여기 제가 생각할 수 있는 가장 좋은 해결책이 있습니다. 함수 작성기를 만들고 대신 호출합니다.이렇게 하면 생성된 각 함수에 대해 서로 다른 환경이 적용되며, 각 함수에는 서로 다른 i가 적용됩니다.
flist = []
for i in xrange(3):
def funcC(j):
def func(x): return x * j
return func
flist.append(funcC(i))
for f in flist:
print f(2)
이것은 부작용과 기능적 프로그래밍을 섞으면 일어나는 일입니다.
한 변수에 계속 합니다.i
그 가치가 변하는 동안.루프의 끝에서 모든 함수는 루프의 마지막 값을 유지하는 동일한 변수를 가리킵니다. 효과는 예제에서 보고된 내용입니다.
을 평가하기 .i
그리고 그 값을 사용합니다, 공통 패턴은 그것을 매개 변수 기본값으로 설정하는 것입니다: 매개 변수 기본값은 다음과 같이 평가됩니다.def
문이 실행되어 루프 변수의 값이 동결됩니다.
다음은 예상대로 작동합니다.
flist = []
for i in xrange(3):
def func(x, i=i): # the *value* of i is copied in func() environment
return x * i
flist.append(func)
for f in flist:
print f(2)
사용 방법은 다음과 같습니다.functools
라이브러리(질문이 제기되었을 때 사용할 수 있었는지 확실하지 않음).
from functools import partial
flist = []
def func(i, x): return x * i
for i in range(3):
flist.append(partial(func, i))
for f in flist:
print(f(2))
예상대로 024를 출력합니다.
이것을 보십시오.
for f in flist:
print f.func_closure
(<cell at 0x00C980B0: int object at 0x009864B4>,)
(<cell at 0x00C980B0: int object at 0x009864B4>,)
(<cell at 0x00C980B0: int object at 0x009864B4>,)
즉, 루프가 끝나면 값이 2인 동일한 i 변수 인스턴스를 가리킵니다.
읽기 쉬운 솔루션:
for i in xrange(3):
def ffunc(i):
def func(x): return x * i
return func
flist.append(ffunc(i))
변수 i가 캡처되고 함수가 호출될 때 바인딩된 값을 반환합니다.함수형 언어에서는 이러한 상황이 발생하지 않습니다. 제가 반발하지 않기 때문입니다.하지만 파이썬과 리스프에서 보았듯이 이것은 더 이상 사실이 아닙니다.
체계 예제와 다른 점은 do 루프의 의미론과 관련이 있다는 것입니다.스킴은 다른 언어와 마찬가지로 기존 i 바인딩을 재사용하는 대신 루프를 통해 매번 새로운 i 변수를 효과적으로 생성합니다.루프 외부에서 생성된 다른 변수를 사용하고 이를 변형하면 구성표에서 동일한 동작을 볼 수 있습니다.루프를 다음으로 교체해 보십시오.
(let ((ii 1)) (
(do ((i 1 (+ 1 i)))
((>= i 4))
(set! flist
(cons (lambda (x) (* ii x)) flist))
(set! ii i))
))
이에 대한 자세한 내용은 여기를 참조하십시오.
[편집] 아마도 do 루프를 다음 단계를 수행하는 매크로로 생각하는 것이 설명하는 더 나은 방법일 것입니다.
- 단일 매개변수(i)를 사용하여 람다를 정의하고, 루프의 본체에 의해 정의된 본체를 사용합니다.
- 적절한 i 값을 모수로 사용하여 해당 람다를 즉시 호출합니다.
즉, 아래의 파이썬에 해당합니다.
flist = []
def loop_body(i): # extract body of the for loop to function
def func(x): return x*i
flist.append(func)
map(loop_body, xrange(3)) # for i in xrange(3): body
i는 더 이상 상위 범위의 변수가 아니라 자체 범위의 완전히 새로운 변수입니다.람다)에 대한 매개변수를 사용하여 관찰한 동작을 확인할 수 있습니다.Python은 이 암묵적인 새로운 범위를 가지고 있지 않기 때문에 for 루프의 본문은 i 변수를 공유할 뿐입니다.
는 모든 한 환경에 입니다.i
변수. 은 각또는 )에 대해 의 환경을 만드는 입니다.솔루션(해결 방법)은 각 함수(또는 람다)에 대해 별도의 환경(스택 프레임)을 만드는 것입니다.
t = [ (lambda x: lambda y : x*y)(x) for x in range(5)]
>>> t[1](2)
2
>>> t[2](2)
4
저는 아직도 왜 어떤 언어에서는 이것이 한 가지 방식으로, 또 다른 방식으로 작동하는지 완전히 확신할 수 없습니다.Common Lisp에서는 Python과 같습니다.
(defvar *flist* '())
(dotimes (i 3 t)
(setf *flist*
(cons (lambda (x) (* x i)) *flist*)))
(dolist (f *flist*)
(format t "~a~%" (funcall f 2)))
"666"을 인쇄합니다(여기서 목록은 1부터 3까지이며 역방향으로 작성됨).Scheme에서는 Perl과 같이 작동합니다.
(define flist '())
(do ((i 1 (+ 1 i)))
((>= i 4))
(set! flist
(cons (lambda (x) (* i x)) flist)))
(map
(lambda (f)
(printf "~a~%" (f 2)))
flist)
인쇄 "6 4 2"
그리고 이미 언급했듯이 Javascript는 Python/CL 캠프에 있습니다.여기에는 여러 언어가 서로 다른 방식으로 접근하는 구현 결정이 있는 것으로 보입니다.정확히 어떤 결정인지 알고 싶습니다.
변수i
함수의 값이 매번 2인 전역입니다.f
이 호출됩니다.
저는 당신이 추구하는 행동을 다음과 같이 구현하는 경향이 있습니다.
>>> class f:
... def __init__(self, multiplier): self.multiplier = multiplier
... def __call__(self, multiplicand): return self.multiplier*multiplicand
...
>>> flist = [f(i) for i in range(3)]
>>> [g(2) for g in flist]
[0, 2, 4]
업데이트에 대한 응답:그것은 세계성이 아닙니다.i
그 자체로 이 동작을 유발하는 것은 f가 호출되는 시간 동안 고정된 값을 갖는 둘러싸는 범위의 변수라는 사실입니다.두 번째 예제에서는 다음과 같은 값을 사용합니다.i
의 범위에서 취함.kkk
기능, 그리고 당신이 기능을 호출할 때 아무것도 변하지 않습니다.flist
.
그 행동의 배후에 있는 이유는 이미 설명되었고, 여러 가지 해결책이 게시되었지만, 저는 이것이 가장 파이썬적이라고 생각합니다(Python의 모든 것이 객체라는 것을 기억하세요!):
flist = []
for i in xrange(3):
def func(x): return x * func.i
func.i=i
flist.append(func)
for f in flist:
print f(2)
클라우디우의 대답은 함수 발생기를 사용하여 꽤 좋지만, 솔직히 파이로의 대답은 해킹입니다. 기본값을 가진 "숨겨진" 인수로 만들고 있기 때문입니다(잘 작동하지만 "파이토닉"은 아닙니다).
위의 솔루션이 생성되는 방식이 마음에 들지 않았습니다.wrappers
순환하여참고: python 3.xx
flist = []
def func(i):
return lambda x: x * i
for i in range(3):
flist.append(func(i))
for f in flist:
print f(2)
언급URL : https://stackoverflow.com/questions/233673/how-do-lexical-closures-work
'programing' 카테고리의 다른 글
서비스:렌더러2에 대한 공급자 없음 (0) | 2023.08.20 |
---|---|
신속한 프로젝트에서 Objective-C 코코아 포드를 사용하는 방법 (0) | 2023.08.20 |
Swift에서 변수와 함께 NSLocalizedString 함수를 사용하는 방법은 무엇입니까? (0) | 2023.08.20 |
통화로 더블 포맷하는 방법 - 스위프트 3. (0) | 2023.08.20 |
Swift Bridging 헤더 가져오기 문제 (0) | 2023.08.20 |