-
Notifications
You must be signed in to change notification settings - Fork 13
/
02.07-Local-Functions.txt
162 lines (105 loc) · 6.92 KB
/
02.07-Local-Functions.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
2.7 Local Functions
2.7 Локальные функции
When we define functions with lambda-expressions, we face a restriction
which doesn’t arise with defun: a function defined in a
lambda-expression doesn’t have a name and therefore has no way of
referring to itself. This means that in Common Lisp we can’t use
lambda to define a recursive function.
Когда мы определяли функции при помощи лямбда-выражений, то
столкнулись с ограничением, которого нет при использовании defun: у
функции определенной как лямбда нет имени, так что нет возможности
ссылаться на себя. Это означает что в Common Lisp мы неможем
использовать лямбды для определения рекурсивных функций.
If we want to apply some function to all the elements of a list, we
use the most familiar of Lisp idioms:
Если мы хотим приенить некоторую функцию ко всем элементам списка, мы
используем наиболее привычный для лиспа стиль:
(mapcar #’(lambda (x)
(+ 2 x))
'(2 5 7 3))
=> (4 7 9 5)
What about cases where we want to give a recursive function as the
first argument to mapcar? If the function has been defined with defun,
we can simply refer to it by name:
Но что поделать, если мы хотим передать первым аргументом mapcar
рекурсивную функцию? Если бы функция была определена через defun, мы
могли бы просто сослаться на нее по имени:
(mapcar #’copy-tree
'((a b) (c d e)))
=> ((A B) (C D E))
But now suppose that the function has to be a closure, taking some
bindings from the environment in which the mapcar occurs. In our
example list+,
Но предположим теперь, что функция должны быть замыканием, содержащим
некоторые свящанные переменные из окружения в котором будет работать
mapcar. В нашем примере list+:
(defun list+ (lst n)
(mapcar #’(lambda (x)
(+ x n))
lst))
the first argument to mapcar,#’(lambda (x) (+ x n)), must be defined
within list+ because it needs to catch the binding of n. So far so
good, but what if we want to give mapcar a function which both needs
local bindings and is recursive? We can’t use a function defined
elsewhere with defun, because we need bindings from the local
environment. And we can’t use lambda to define a recursive function,
because the function will have no way of referring to itself.
первый аргумент mapcar, #'(lambda (x) (+ x n)), должен быть определен
внутри list+, потому что ему нужно связанное значение n. Пока что все
хорошо, но что поделать, если нам нужно передать в mapcar и локально
связанную переменную и рекурсивную функцию? Мы не можем использовать
функцию, определенную где-то при помощи defun, потому что нам нужны
локально связанные переменные и мы не можем использовать лямбду для
определения рекурсивной функции, потому что она неможет ссылаться на
себя.
Common Lisp gives us labels as a way out of this dilemma. With one
important reservation, labels could be described as a sort of let for
functions. Each of the binding specifications in a labels expression
should have the form
Common Lisp дает нам labels для разрешения этой дилеммы. С одной
важной оговоркой, labels должны быть описаны в форме похожей на
let. Каждое описание связывания в выражении должно быть следующего
вида
(<name> <parameters> . <body>)
Within the labels expression, <name> will refer to a function
equivalent to:
Внутри labels выражение <name> ссылаться на функцию, эквивалентную
следующей:
#'(lambda <parameters> . <body>)
So for example:
В качестве примера:
(labels ((inc (x) (1+ x)))
(inc 3))
=> 4
However, there is an important difference between let and labels.Ina
let expression, the value of one variable can’t depend on another
variable made by the same let—that is, you can’t say
Тем не менее есть важное различие между let и labels. Внутри let
выражения значение одной переменной неможет зависеть от какой-либо
другой объявленной внутри одной let формы, это значит что вы неможете
написать
(let ((x 10) (y x))
y)
and expect the value of the new y to reflect that of the new x. In
contrast, the body of a function f defined in a labels expression may
refer to any other function defined there, including f itself, which
makes recursive function definitions possible.
и ожидать что значение новой переменной y будет содержать значение
новой x. С другой стороны тело функции f определенной внутри выражения
labels может ссылаться на любую другую функцию, определенную там же,
влючая саму себя, что делает возможным описание рекурсивных функций.
Using labels we can write a function analogous to list+, but in which
the first argument to mapcar is a recursive function:
Используя labels мы можем написать функцию, аналогичную list+, но в
которой первый аргумент mapcar будет рекурсивной функцией:
(defun count-instances (obj lsts)
(labels ((instances-in (lst) (if (consp lst)
(+ (if (eq (car lst) obj) 1 0) (instances-in (cdr lst)))
0)))
(mapcar #’instances-in lsts)))
This function takes an object and a list, and returns a list of the
number of occurrences of the object in each element:
Следующая функция берет объект и список и возвращает список чисел,
соответствующих числу вхождений объекта в каждый элемент:
(count-instances 'a '((a b c) (darpa)(dar) (a a)))
=> (1 2 1 2)