Как лексические закрытия работают?

Если вы хотите реализовать метод подкачки для объектов Integer, вы должны обернуть значения в массив (или ArrayList) и поменять его внутри массива. Вот адаптация вашего кода:

public class Main {

    static void swap (Integer[] values) {
        if ((values == null) || (values.length != 2)) {
          throw new IllegalArgumentException("Requires an array with exact two values");
        }

        Integer t = values[0];
        values[0] = values[1];
        values[1] = t;
    }

    public static void main(String[] args) {
       Integer a = 1;
       Integer b = 2;
       Integer[] integers= new Integer[]{a,b};
       swap(integers);
       System.out.println("a=" + integers[0] + " b=" + integers[1]);
    } 
}

(просто добавил этот ответ, потому что Svish упомянул, что «Вы действительно не можете создать метод swap в Java» фг )

144
задан martineau 12 September 2017 в 22:39
поделиться

7 ответов

Python на самом деле ведет себя, как определено. Три отдельных функции создаются, но каждый из них имеет закрытие среды, они определяются в - в этом случае, глобальная среда (или среда внешней функции, если цикл помещается в другой функции). Это - точно проблема, хотя - в этой среде, я видоизменен , и закрытия весь отсылают к тому же меня .

Вот лучшее решение, которое я могу предложить - создают функционального создателя и вызывают это вместо этого. Это вызовет различные среды для каждой из созданных функций, с отличающийся я в каждом.

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)

Это - то, что происходит, когда Вы смешиваете побочные эффекты и функциональное программирование.

148
ответ дан Claudiu 12 September 2017 в 22:39
поделиться
  • 1
    Но что, если это было это: f (функционируют = > \& func); тогда в функции, f, это было бы это? sub f {$args {функция}-> ();} – Kys 5 August 2009 в 18:44

Функции, определяемые в цикле продолжают получать доступ к той же переменной 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)
153
ответ дан piro 12 September 2017 в 22:39
поделиться
  • 1
    Kys: Да, '-> () ' разыменовывает ссылку кода и называет его. Аргументы функциональному движению между parens, как you' d ожидают. – runrig 5 August 2009 в 18:57

посмотрите на это:

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, как только цикл закончен.

А читаемое решение:

for i in xrange(3):
        def ffunc(i):
            def func(x): return x * i
            return func
        flist.append(ffunc(i))
13
ответ дан Null303 12 September 2017 в 22:39
поделиться

То, что происходит, - то, что переменная, я получен, и функции, возвращает значение, это связывается с тем, в то время, когда это называют. На функциональных языках никогда не возникает этот вид ситуации, поскольку я не был бы восстановлением. Однако с Python, и также как Вы видели с шепелявостью, это больше не верно.

различие с Вашим примером схемы относится к семантике, действительно циклично выполняются. Схема эффективно создает новое я переменная каждый раз через цикл, вместо того, чтобы снова использовать существующее я связывающий как с другими языками. Если Вы будете использовать различную переменную, созданную внешний к циклу, и видоизменять его, Вы будете видеть то же поведение в схеме. Попытайтесь заменить свой цикл:

(let ((ii 1)) (
  (do ((i 1 (+ 1 i)))
      ((>= i 4))
    (set! flist 
      (cons (lambda (x) (* ii x)) flist))
    (set! ii i))
))

Смотрят здесь для некоторого дальнейшего обсуждения этого.

[Редактирование] Возможно лучший способ описать его состоит в том, чтобы думать, действительно циклично выполняются как макрос, который выполняет следующие шаги:

  1. Определяют лямбду, берущую единственный параметр (i), с телом, определенным телом цикла,
  2. непосредственный вызов той лямбды с соответствующими значениями меня как ее параметр.

т.е. эквивалент ниже Python:

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

я больше не является тем от родительского объема, но совершенно новой переменной в ее собственном объеме (т.е. параметр к лямбде) и таким образом, Вы получаете поведение, которое Вы наблюдаете. Python не имеет этого неявного нового объема, таким образом, тело для цикла просто совместно использует меня переменная.

7
ответ дан Brian 12 September 2017 в 22:39
поделиться
  • 1
    Что, если бы я передавал функцию как часть хеша, затем это было бы похоже на это? $args {функция}-> (); – Kys 5 August 2009 в 18:23

Проблема состоит в том, что все локальные функции связывают с той же средой и таким образом с тем же i переменная. Решение (обходное решение) состоит в том, чтобы создать отдельные среды (стековые фреймы) для каждой функции (или лямбда):

t = [ (lambda x: lambda y : x*y)(x) for x in range(5)]

>>> t[1](2)
2
>>> t[2](2)
4
2
ответ дан Rafał Dowgird 12 September 2017 в 22:39
поделиться

Я полностью все еще не убежден, почему на некоторых языках это прокладывает себе путь, и некоторым другим способом. В языке 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)))

Печать "6 6 6" (отмечают, что здесь список от 1 до 3, и создан наоборот"). В то время как в Схеме это работает как в 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. Кажется, что здесь существует решение реализации, к которому различные языки приближаются отличными способами. Я хотел бы понять то, что является решением, точно.

4
ответ дан Eli Bendersky 12 September 2017 в 22:39
поделиться

Переменная 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.

1
ответ дан Alex Coventry 12 September 2017 в 22:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: