不了解python的内部函数

2024-03-28 19:48:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我有个问题:

cons(a, b) constructs a pair, and car(pair) and cdr(pair) returns the first and last element of that pair. For example, car(cons(3, 4)) returns 3, and cdr(cons(3, 4)) returns 4.

Given this implementation of cons:

def cons(a, b):
    def pair(f):
        return f(a, b)
    return pair

Implement car and cdr.

我不懂这个功能。在

它有一个内部函数,并在返回中调用另一个函数。据我所知,内部函数是,这些函数应该依赖于它们上面的函数。在本例中,cons(..)。在

但是函数没有使用ab。为什么函数f在那里?任务是实现carcdr,并且应该给出函数f。在

有人能解释一下这个功能吗?我该如何开始这项任务呢?在


Tags: andofthe函数功能returndefcar
3条回答

这就是所谓的闭包。两个基本要素

  • pair函数知道a和{}的值。在这方面,它们就像局部变量
  • cons函数返回一个函数,而不是一个值。你必须再次调用结果

所以,当你调用cons(a, b)时,你得到了一个对ab执行某些操作的函数,只有它还不知道是什么。你必须传递另一个函数。例如

 my_f = cons(1, 2)
 result = my_f(lambda x, y: x + y)
 print(result) # 3

这和你的任务有什么关系还不清楚。我只假设对于car,您只需要元素a(头部),而对于{},您需要元素b(列表的其余部分)。因此,函数只返回一个参数而忽略另一个参数。例如

^{pr2}$

首先:Python函数对象是一类对象。一条def语句将生成一个新的函数对象,您可以使用函数名检索该对象:

>>> def foo():
...     return 'foo was called'
...
>>> foo
<function foo at 0x11b3d8ae8>

这意味着您也可以将该对象分配给其他名称,并且可以将它们作为参数传递给函数调用。然后,您可以稍后通过向引用添加(...)来调用函数对象:

^{pr2}$

函数名在当前命名空间中分配给。在模块中,这是全局变量,但在cons这样的函数中,名称作为局部名称添加。return pair函数中的{}然后将函数对象{}返回给调用者。在

而函数参数,如fab也是变量;如果将函数对象作为参数传入,则parameter_name(...)将调用paramater_name,并传入...部分中的任何参数。f(a, b)调用f,并传入a和{}。在

下一个要理解的项目是closures;闭包是附加到函数对象的额外名称空间,用于来自周围范围的变量。在

在下面的示例中,spambar函数闭包中的一个名称:

>>> def foo():
...     spam = 'Vikings'
...     def bar():
...         return spam
...     return bar
...
>>> foo()
<function foo.<locals>.bar at 0x11b44bf28>
>>> foo()()
'Vikings'

调用foo()返回一个新的函数对象,bar()函数位于foo()内。调用返回的函数对象生成'Vikings',即foo函数中spam变量的值。bar()是如何获得访问权限的?通过关闭:

>>> foo().__closure__
(<cell at 0x11b3c05b8: str object at 0x11b469180>,)
>>> foo().__closure__[0].cell_contents
'Vikings'

因此,嵌套函数可以通过闭包访问周围范围内的名称。(旁注:存储在闭包中的不是,而是变量。变量可以随时间而变化,就像以后访问名称的其他变量一样,将反映新值;如果spam稍后被更改,再次调用bar()将返回新值)。在

现在到您的函数:cons()创建一个内部函数pair(),并且pair()可以通过其闭包访问参数a和{}:

>>> 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和{}。让我们看看当我们传入print时会发生什么。在

print也是一个函数,它是一个可以调用的对象,它将参数写入控制台,中间有空格:

>>> print
<built-in function print>
>>> print('arg1', 'arg2')
arg1 arg2

如果将其传递给pair()返回的pair()函数,则可以看到f(a, b)的作用:

>>> pair_42_81(print)
42 81

传递给print,被分配给f,而{}与{}完全相同。在

我们可以看到,调用了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和{},并将使用这两个参数调用给定的对象f。在

接下来是cdr()car(),它们将返回对中的第一个或最后一个元素。如果cons(a, b)产生{}返回{},那么cdr()和{}必须各自创建一个函数来传递给pair(),该函数将提取a或{}。在

因此在每个函数中创建一个嵌套函数,并使用该函数使cdr()和{}调用pair()。嵌套函数执行选择a或{}的工作,并返回该值。然后将调用结果返回给外部世界:

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),现在只返回{}:

>>> cdr(cons(42, 81))
81

您可能对这些操作的背景感兴趣;carcdrcons来自LISP,其中cons a b构造了一个带有两个指针的单元格(解释了名称),而car(在IBM 704指令集LISP中,表示寄存器号的地址部分的内容)和cdr(在704语言中,表示寄存器号的减量部分的内容)取这样一个单元的第一部分和剩余部分。见this Wikipedia article on the names。在

cons是一个接受两个参数a和{}并返回一个函数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的括号是(...)的两倍,一次用于它自己的调用,另一次用于返回的函数调用。在

请注意this answer以便能够调用car(cons(2, 3))。您可能还对Why would a program use a closure?感兴趣

相关问题 更多 >