2013-12-18 2 views
5

Я очень новичок в функциональных концепциях программирования и смотрел презентацию Нила Форда в youtube. Там он говорит о прилавке, чтобы продемонстрировать фрагмент кода без использования глобального состояния (в 20:04). Исходя из Java-мира, мне сложно понять концепцию здесь и как счетчик увеличивается. Ниже приведен соответствующий кодСчетчик функционального стиля в Groovy

def makeCounter() { 
    def very_local_variable = 0; 
    return {very_local_variable += 1} 
} 

c1 = makeCounter() 
c1() 
c1() 
c1() 

c2 = makeCounter() 
println "C1 = ${c1()}, C2 = ${c2()}" 

Далее он скажет, что C1 = 4 и C2 = 1 будут напечатаны. Как это произошло? Я уверен, что мое отсутствие понимания здесь проистекает из, вероятно, концептуального отказа в том, как работает Groovy или, возможно, есть что-то общее в функциональных языках, таких как Groovy, Scala и т. Д. Локальная переменная внутри метода поддерживает свое состояние до тех пор, пока функция не будет вызвана снова и присваивается другой переменной? (Поиск в google с «функциональным счетчиком groovy | scala» ничего не приносит)

+5

Читайте это: http://groovy.codehaus.org/Closures. –

+1

Особенно «свободные переменные» _ раздел –

+0

merci. Я сейчас просвещен! – zencv

ответ

7

Я не знаю, почему у этого вопроса нет ответа, но он, вероятно, заслуживает хотя бы одного для будущих посетителей.

То, что вы написали выше, является простым примером closure. Закрытие - это языковая агностическая идея, не привязанная к Groovy вообще. Вы используете их все время, например. в JavaScript.

Закрытие по существу является функцией или ссылкой на функцию вместе со ссылочной средой.

В вашем примере каждый звонок makeCounter вводит новый локальный very_local_variable, это ясно, я думаю. То, что вы делаете во второй строке этой функции, - это возврат замыкания ({ .. }), который не принимает аргументов и возвращает локальную переменную, увеличивающуюся на 1. У закрывания есть доступ к этой переменной так долго, как она существует (она является частью справочная среда).

Каждый вызов makeCounter создает новую локальную переменную и возвращает новое закрытие, поэтому каждый из них работает с собственной локальной переменной/средой.

Собственно, если вы приехали с острова Java, это не должно быть для вас новым. У вас есть анонимные классы, которые могут обращаться к конечным переменным, например.

Runnable makeCounter() { 
    final int[] very_local_variable = {0}; 
    return new Runnable() { 
     @Override 
     public void run() { 
      very_local_variable[0] += 1; 
     } 
    }; 
} 


Runnable c1 = makeCounter(); 

c1.run(); 
c1.run(); 
c1.run(); 

Декларирование локальную переменную как массив, возвращая его в качестве Runnable функтора и так далее .. все это происходит от дизайна и ограничений в Java, но, в сущности, это очень близко к коду. Java 8 еще больше расширяет разрыв:

+0

Я хотел ответить сам, но вы были быстрее :-). В контексте таких языков, как Groovy, Javascript и т. д., я понимаю, как замыкания могут быть использованы таким образом. Но в вашем Java, например, я понимаю, почему нам нужен Anon. class, final var., array и т. д., но как мы можем использовать счетчик 'very_local_variable'? Я использую Анона. класс и лямбда, но это значит, что нужно передавать поведение методу вместе с данными, которые ему нужно манипулировать. В любом случае, я думаю, нам нужно изменить его как «Runnable makeCounter (final int [] very_local_variable)» и передать его при вызове 'makeCounter()' или? – zencv

+0

* но как мы можем когда-либо использовать 'very_local_variable counter' * ... что вы имеете в виду? Точно так же вы использовали бы члена класса. Помните, хотя, * Closures - объекты бедного человека и наоборот. * :-) – emesx

Смежные вопросы