Python:使用cons,car和cdr进行函数式编程 输出: cons::(a, b) -> (((a, b) -> c) -> c) cons car

问题描述

有这样的问题:

cons(a,b)构造一对,car(pair)cdr(pair)返回 该对的第一个和最后一个元素。例如,car(cons(3,4)) 返回3,而cdr(cons(3,4))返回4

现在,我已经看到了解决方案,但是我想知道是否有人可以解释如何思考以达到解决方案?

  1. cdr(cons(3,4)):这两个函数的评估顺序是什么?我通常会认为首先对cons(3,4)求值,但是在这种情况下这没有意义,因为cons(3,4)返回的函数将参数3和4进行了“集成”,因此没有任何方法排除争论。
  2. 在我看来car(f)返回一个函数,那么cons(3,4)如何返回3编辑:错字应为car(cons(3,4)),而不是cons(3,4)
  3. 我显然想解决此问题,因为我想学习Python,但是您会建议我跳过这些问题吗?我很想阅读这里的内容:Why program functionally in Python?

解决方案:

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

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

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

print(car(cons(3,4)))
Output: 3
print(cdr(cons(3,4)))
Output: 4

解决方法

a(b())中,b将始终被首先评估。我不知道Python中有一个例外。 a必须是一个宏,相反的情况才能成立。

请注意cdrcar的参数名称是:f,如“函数”中所示。每个函数都接受一个函数作为参数。不过,这是可行的,因为您注意到cons返回了一个函数。

car(cons(3,4))中,cons返回一个函数(本地称为pair)。然后将该功能提供给carcar在这里使用它:f(pair)。在这种情况下,f是传入的函数。此处的复杂部分是f是接受另一个函数并使用两个参数调用它的函数。这两个参数是最初提供给cons的数据:34


cons(3,4)不会返回3,而car(cons(3,4))会返回cons(3,4)car返回一个函数,该函数对提供给它的数据起作用。在这种情况下,pair的{​​{1}}函数最终会丢弃第二个传递的参数(4),而是返回第一个传递的参数(3)。


是的,暂时远离这样的代码一段时间。传递函数非常有用,但是此代码更像是一种实验玩具。这是一种显示样式的理论代码(基于术语,源自诸如Scheme之类的Lisp)。有很多简单的方法可以达到相同的最终结果。

实践Higher Order Functions的简单示例(例如mapreduce),使他们精通它们,然后重新阅读此代码。仍然很难理解(因为该代码本身不易于理解),但是更有意义。

,

您显示的问题也可以通过这种方式解决。

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

def car(pair):
    return pair[0]

def cdr(pair):
    return pair[1]

这是您将如何使用它:

lst = cons(1,cons(2,3))

# Get the first element of lst
print(car(lst))

# Get the second element of lst
print(car(cdr(lst)))

# Get the last element of lst
print(cdr(cdr(lst)))

输出:

1
2
3

我仅显示此内容,以便您可以看到解决该问题的方法不止一种,而发现的方法很少在python中完成。任何想在python中解决此问题的人都将有99%的时间按照我在此处所示的方法进行操作。

现在解决您的问题。


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

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

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

首先,我们使用一些haskell函数符号来讨论这些函数,以便您可以看到这些函数的完整类型:

cons::(a,b) -> (((a,b) -> c) -> c)

cons是一个带有两个参数ab的函数,然后它返回一个函数f,该函数带有另一个在给定参数({{ 1}},a)返回b,其中c可以是ca或其他。 b然后返回f的值。

好吃!

另一种思考方式是c返回的函数f((a,b) -> c) -> c)用于将consa转发给任何运算符(或映射功能)要作用于b上。该运算符返回consc然后简单返回此映射函数返回的值,恰好是f

暂时不要担心c是什么。只需考虑一下将函数应用于c的结果即可。

cons

car::(((a,b) -> a) -> a) -> a定义了从car(a,b)的可能映射,并使用此映射返回调用c的值。

f使用函数car,该函数想要从输入f到某些输出(a,b)的映射。在这种情况下,c将映射定义为car,这意味着传递给(a,b) -> a的任何函数f都将返回car的第一个参数,即仅(a,b)。这就是a将返回的内容。

car

类似于cdr::(((a,b) -> b) -> b) -> b,但由car定义的映射返回cdr而不是b


注意acdr的输入与car返回的函数(f)有多相似?这就是为什么我只是将他们的输入称为cons


现在回答您的一些问题:

f:这两个函数按什么顺序求值?我通常会认为首先对cdr(cons(3,4))求值,但是在这种情况下这没有意义,因为cons(3,4)返回的函数将参数3和4进行了“集成”,因此没有任何方法排除参数。

根据我上面的解释,从cons(3,4)返回的函数与cons期望的函数类型完全相同。现在,cdr所要做的就是提供到cdr的映射函数,并返回f返回的内容。

在我看来f返回一个函数,那么car(f)如何返回cons(3,4)编辑:错别字应为3,而不是car(cons(3,4))

cons(3,4)不一定返回一个函数。请参阅上面的类型签名。它只返回car(f)返回的值,如果恰好是一个函数,则它将返回一个函数。

通常,f返回car的第一个元素。在这种情况下,由于cons返回一个函数(cons(3,4))并将此函数传递给f,因此car将为该函数提供另一个选择第一个函数的函数。它是参数,在这种情况下为car。现在,此33的结果。

我希望一切都可以清除。

,

您粘贴的代码应按以下方式使用:

首先,您定义您的对:

f = cons(3,4)

然后,您定义一个可成对使用的函数:

add = lambda x,y: x + y

现在,您可以像这样使用“对”:

f(add)

输出:

7

因此,它的作用是:将对转换为一个函数,该函数可以对以定义的对作为参数的对“执行”函数。 carcds实际上可以“转换”成对函数并返回一个元素。

编辑:

如果您对lambda表达式不熟悉,请参阅this tutorial

现在,您也可以选择

def add(x,y):
    return x + y

并以相同的方式使用它。 :)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...