我有这个问题:
cons(a, b)
constructs a pair, andcar(pair)
andcdr(pair)
returns the first and last element of that pair. For example,car(cons(3, 4))
returns3
, andcdr(cons(3, 4))
returns4
.Given this implementation of cons:
def cons(a, b): def pair(f): return f(a, b) return pair
Implement
car
andcdr
.
我没有功能。
它具有一个内部函数,并在返回中调用另一个函数。据我了解,内部功能是这些功能应取决于它们上面的功能。在这种情况下,使用
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'
函数名称在当前 namespace 中分配给它。在模块中,这是全局变量,但是在诸如
cons
之类的函数中,该名称被添加为本地名称。 return pair
函数中的cons
然后将函数对象pair
返回给调用者。函数参数,例如
f
和a
和b
也是变量。如果传入函数对象作为参数,则parameter_name(...)
将调用paramater_name
并传入...
部分中的所有参数。 f(a, b)
调用f
并传递a
和b
。下一个要了解的项目是closures;闭包是附加到函数对象的一个额外的命名空间,用于来自周围作用域的变量。
在以下示例中,
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'
因此,嵌套函数可以通过闭包访问周围范围的名称。 (注意:不是存储在闭包中的值,而是变量。变量可以随时间变化,就像以后访问该名称的其他变量将反射(reflect)新值一样;如果稍后更改了
spam
,则再次调用bar()
将返回新值)。现在到您的函数:
cons()
创建一个内部函数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 a b
构造一个带有两个指针(解释名称)的单元,以及car
(意味着在LISP的IBM 704指令集中创建了注册号的地址部分的内容)和cdr
(含义是704语言中寄存器编号的减量部分的内容)占据了该单元格的第一部分和其余部分。参见this Wikipedia article on the names。
关于python - 不了解python的内部功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52481607/