В Erlang оператор =
является одновременно присваиванием и утверждением.
Если я сделаю это:
A = 1,
A = 2,
моя программа выйдет из строя. Я только что сказал ему, что A = 1
, который, когда A
не привязан (еще не существует в виде метки), ему теперь присваивается значение 1 навсегда и навсегда - до тех пор, пока объем выполнения не изменится. Итак, когда я говорю ему, что A = 2
, он пытается утверждать, что значение A
равно 2, но это не так. Таким образом, мы получаем вылет из-за плохого матча.
Область видимости в Erlang определяется двумя вещами:
- Определение текущей функции. Эта область действия является абсолютной на время определения функции.
- Определение текущего понимания лямбда или списка. Эта область является локальной для лямбда-выражения, но также закрывает любые значения из внешней области, на которые имеются ссылки.
Эти области всегда заменяются в момент объявления тем, что находится во внешней области. Так мы закрываем анонимные функции. Например, предположим, что у меня есть сокет, через который я хочу отправить список данных. Сокет уже привязан к имени переменной Socket
в заголовке функции, и мы хотим использовать операцию со списком для сопоставления списка значений для отправки с побочным эффектом отправки через этот конкретный сокет. Я могу закрыть значение сокета в теле лямбды, что имеет эффект каррирования этого значения из более общей операции «отправки некоторых данных»:
send_stuff(Socket, ListOfMessages) ->
Send = fun(Message) -> ok = gen_tcp:send(Socket, Message) end,
lists:foreach(Send, ListOfMessages).
Каждая итерация операции со списком lists:foreach/2
может принимать только функцию арности 1 в качестве своего первого аргумента. Мы создали замыкание, которое уже внутренне фиксирует значение Socket
(потому что оно уже было связано во внешней области) и объединяет его с несвязанной внутренней переменной Message
. Также обратите внимание, что мы проверяем, работает ли gen_tcp:send/2
каждый раз в лямбда-выражении, утверждая, что возвращаемое значение gen_tcp:send/2
было действительно ok
.
Это очень полезное свойство.
Имея это в виду, давайте посмотрим на ваш код:
1> Total = 15.
2> Calculate = fun(Number)-> Total = 2 * Number end.
3> Calculate(6).
В приведенном выше коде вы только что присвоили значение Total
, что означает, что вы создали метку для этого значения (точно так же, как мы присвоили Socket
в приведенном выше примере). Позже вы утверждаете, что значение Total
- это то, чем может быть результат 2 * Number
, что никогда не может быть истинным, поскольку Total
было целым числом, поэтому 2 * 7.5
тоже не будет сокращать его, потому что результат будет быть 15.0
, а не 15
.
1> Calculate = fun(Number)-> Total = 2 * Number end.
2> Total = 15.
3> Calculate(6).
Однако в этом примере у вас есть внутренняя переменная с именем Total
, которая не закрывается ни одним значением, объявленным во внешней области. Позже вы объявляете метку во внешней области видимости с именем Total
, но к этому времени определение лямбда в первой строке было преобразовано в абстрактную функцию, а метка Total
, использованная там, была полностью предоставлена к неизменяемому пространству определения новой функции представляет присваивание Calculate
. Таким образом, никакого конфликта.
Рассмотрим, что происходит, например, при попытке сослаться на внутреннее значение из понимания списка:
1> A = 2.
2
2> [A * B || B <- lists:seq(1,3)].
[2,4,6]
3> A.
2
4> B.
* 1: variable 'B' is unbound
Это не то, что вы ожидаете, скажем, от Python 2:
>>> a = 2
>>> a
2
>>> [a * b for b in range(1,4)]
[2, 4, 6]
>>> b
3
Между прочим, это было исправлено в Python 3:
>>> a = 2
>>> a
2
>>> [a * b for b in range(1,4)]
[2, 4, 6]
>>> b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
(И я бы также предоставил пример JavaScript для сравнения, но правила области видимости настолько безумны, что даже не имеет значения ...)
person
zxq9
schedule
05.10.2017