У меня есть эта проблема:
cons(a, b)
создает пару, аcar(pair)
иcdr(pair)
возвращает первый и последний элементы этой пары. Например,car(cons(3, 4))
возвращает3
, аcdr(cons(3, 4))
возвращает4
.Учитывая эту реализацию минусов:
def cons(a, b): def pair(f): return f(a, b) return pair
Реализуйте
car
иcdr
.
Я не получаю эту функцию.
Он имеет внутреннюю функцию и вызывает другую функцию в возврате. Насколько я понял, внутренние функции заключаются в том, что эти функции должны зависеть от функций над ними. В этом случае на cons(..)
.
Но функция не использует a
или b
. И почему здесь функция f
? Задача состоит в том, чтобы реализовать car
и cdr
, и функция f
должна быть предоставлена.
Так может кто-нибудь объяснить мне эту функцию? И как я мог начать с задания?
Прежде всего: объекты функции Python являются объектами первого класса. В выражении def
появляется новый объект функции, и вы можете получить этот объект, используя имя функции:
>>> def foo():
... return 'foo was called'
...
>>> foo
<function foo at 0x11b3d8ae8>
Это означает, что вы также можете назначить этот объект другим именам, и вы можете передать их в качестве параметров для вызовов функций. Затем вы можете вызвать объект функции, добавив (...)
в ссылку:
>>> bar = foo
>>> bar()
'foo was called'
Имя функции назначается в текущем пространстве имен. В модуле, что глобальные переменные, но в такой функции, как cons
, имя добавляется как локальное. return pair
в функции cons
затем возвращают pair
объекта функции вызывающему.
И параметры функции, такие как f
и a
и b
, также являются переменными; если вы передадите объект функции в качестве параметра, тогда parameter_name(...)
вызовет paramater_name
и передаст любые аргументы в ...
f(a, b)
вызывает f
и переходит в a
и b
.
Следующим пунктом для понимания являются закрытие; закрытие - дополнительное пространство имен, прикрепленное к объектам функции, для переменных из окружающей области.
В следующем примере spam
- это имя в закрытии функции bar
:
>>> def foo():
... spam = 'Vikings'
... def bar():
... return spam
... return bar
...
>>> foo()
<function foo.<locals>.bar at 0x11b44bf28>
>>> foo()()
'Vikings'
Вызов foo()
возвращает новый объект функции; функция bar()
внутри foo()
. Вызов, что возвращаемый объект функции производит 'Vikings'
, значение spam
переменной в функции foo
. Как bar()
получил к этому доступ? Через закрытие:
>>> foo().__closure__
(<cell at 0x11b3c05b8: str object at 0x11b469180>,)
>>> foo().__closure__[0].cell_contents
'Vikings'
Таким образом, вложенные функции могут иметь доступ к именам из окружающей области через закрытие. (Сторона примечания: это не значение, которое хранится в закрытии, это переменная. Переменная может меняться со временем, и, как и другие переменные, получающие доступ к имени позже, будут отображать новое значение, если впоследствии spam
был изменен, вызывающая bar()
снова вернет новое значение).
Теперь к вашей функции: cons()
создает внутреннюю pair()
функций pair()
, а pair()
имеет доступ к параметрам a
и b
через ее закрытие:
>>> def cons(a, b):
... def pair(f):
... return f(a, b)
... return pair
...
>>> cons(42, 81)
<function cons.<locals>.pair at 0x11b46f048>
>>> pair_42_81 = cons(42, 81)
>>> pair_42_81.__closure__
(<cell at 0x11b3c02b8: int object at 0x10f59a750>, <cell at 0x11b3c05b8: int object at 0x10f59ac30>)
>>> pair_42_81.__closure__[0].cell_contents
42
>>> pair_42_81.__closure__[1].cell_contents
81
Функция pair()
принимает параметр f
и вызывает этот параметр, проходящий через a
и b
. Давайте посмотрим, что произойдет, когда мы перейдем к print
.
print
является функцией, это объект, который вы можете вызвать, и он записывает аргументы в консоль с пробелами между ними:
>>> print
<built-in function print>
>>> print('arg1', 'arg2')
arg1 arg2
Если вы передали это функции pair()
которая возвращает cons()
, вы можете увидеть, что делает f(a, b)
:
>>> pair_42_81(print)
42 81
print
, переданный в pair()
, присваивается f
, а f(a, b)
- это то же самое, что и print(a, b)
.
Мы видим, что print()
вызывается, потому что значения выписываются на консоль. Но вы также можете создать функцию, которая возвращает новое значение. Скажем, у вас есть функция, которая объединяет два числа и возвращает это значение:
>>> def add(first, second):
... return first + second
...
>>> add(42, 81)
123
>>> pair_42_81(add)
123
Мы можем вызвать функцию напрямую, и 123
возвращается, или мы можем иметь pair_42_81()
сделать это для нас, и тот же результат возвращается нам. Просто!
Все это работает, потому что функции являются объектами и могут передаваться как другие переменные, и потому что у pair_42_81
есть a = 42
и c = 81
хранящиеся в закрытии, и будут использовать их для вызова данного объекта f
с этими двумя аргументами.
Далее следуют cdr()
и car()
, которые возвращают либо первый, либо последний элемент пары. Если cons(a, b)
создает pair(f)
возвращающую f(a, b)
, то cdr()
и car()
должны каждый создать функцию для передачи в pair()
которая будет извлекать либо a
либо b
.
Поэтому создайте вложенную функцию в каждой, а также с помощью функции cdr()
и car()
pair()
с этой функцией. Вложенная функция выполняет работу по выбору a
или b
и возвращает это значение. Затем верните результат вызова во внешний мир:
def car(pair):
def return_first(a, b):
return a
return pair(return_first)
def cdr(pair):
def return_last(a, b):
return b
return pair(return_last)
pair(return_first)
вызывает return_first(a, b)
, a
возвращается, и car()
может вернуть это вызывающему:
>>> car(cons(42, 81))
42
и то же самое относится к pair(return_last)
, только теперь возвращается b
:
>>> cdr(cons(42, 81))
81
Возможно, вас интересует предыстория этих операций; car
, cdr
и cons
происходят из LISP, где cons ab
Создает ячейку с двумя указателями (объясняя имя) и car
(что означает, что в состав созданного набора команд IBM 704 LISP было создано содержимое адресной части номера регистра) и cdr
(это означает, что часть Сокращающей части номера регистра на языке 704) принимает первую и оставшуюся части такой ячейки. См. Статью в Википедии об именах.
Это называется замыканием. Два основных элемента
pair
функция знает значения a
и b
. В этом отношении они похожи на локальные переменныеcons
возвращает функцию, а не значение. Вы должны снова вызвать результат Итак, когда вы вызываете cons(a, b)
вы получаете функцию, которая что-то делает для a
и b
, но пока она еще не знает. Для этого вам нужно передать еще одну функцию. Например
my_f = cons(1, 2)
result = my_f(lambda x, y: x + y)
print(result) # 3
Как это связано с вашим назначением, не очень понятно. Я бы предположил, что для car
вам нужен только элемент a
(head), а для cdr
вы хотите элемент b
(остальная часть списка). Таким образом, функции просто вернут один аргумент и игнорируют другой. Например
car_f = lambda x, y: x
cdr_f = lambda x, y: y
cons
возвращает функциональную кодировку кортежа, где вы извлекаете первый или второй элемент кортежа, вызывая кортеж на соответствующем методе доступа. Это аннулирование обычной реализации, ориентированной на данные. Вы не называете car(some_tuple)
; вместо этого вы вызываете some_tuple(car)
.
cons
- это функция, которая принимает два аргумента: a
и b
и возвращает pair
функций.
Функция pair
принимает функцию f
в качестве аргумента, который потребляет два аргумента.
def cons(a, b):
def pair(f):
return f(a, b)
return pair
f = lambda n, m: n**2 + m**3
car = lambda n, m: n
cdr = lambda n, m: m
print(cons(2, 3)(f))
print(cons(2, 3)(car))
print(cons(2, 3)(cdr))
f
возвращает 31 = 2**2 + 3**3
Обратите внимание, что cons
имеют два раза скобки (...)
- один раз, для собственного вызова и в другое время для вызова возвращенных функций.
Обратите внимание на этот ответ, чтобы иметь возможность вызвать car(cons(2, 3))
. Вы также можете быть заинтересованы в Почему программа использует закрытие?
cons(2,3)
для такой функции, как f
; его цель - взять в качестве аргумента car
или cdr
для получения соответствующего значения из кортежа.
car(cons(2,3))
, хотя; car
и cdr
нужен дополнительный уровень косвенности, чтобы позволить это.)
cons
должны возвращать кортеж, а не функциюcons()
является частью их назначения. Таким образом,cdr()
иcar()
должны передать функцию, которая принимает первый или последний переданный аргумент.