2

有这样的问题:

cons(a, b)构造一个对,car(pair)cdr(pair)返回该对的第一个和最后一个元素。例如,car(cons(3, 4)) 返回3cdr(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,但是你会建议我跳过这些问题吗?我很想通过阅读这里来做到这一点:为什么要在 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
4

3 回答 3

3

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

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接受另一个函数,当给定参数 ( a, b) 时,返回c,其中c可能是aorb或其他东西。f然后返回 的值c

多嘴啊!

另一种思考方式是,返回的函数f( ((a, b) -> c) -> c)cons用于转发a和转发b给任何想要对 a 进行操作的运算符(或映射函数)cons。此运算符返回c. f然后 simple 返回此映射函数返回的任何内容,恰好是c.

现在不用担心是什么c。只需将其视为将函数应用于 a 的结果cons

car::(((a, b) -> a) -> a) -> a

car定义一个从(a,b)to的可能映射,并返回使用此映射c调用的值。f

car接受一个f想要从输入映射(a,b)到某个输出的函数c。在这种情况下,car将映射定义为这意味着传递给的(a, b) -> a任何函数都将返回 的第一个参数,即. 这就是将要返回的东西。fcar(a,b)acar

cdr::(((a, b) -> b) -> b) -> b

类似于,但由返回而不是car定义的映射。cdrba


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


现在回答你的一些问题:

cdr(cons(3, 4)): 这两个函数的评估顺序是什么?我通常会认为cons(3, 4)首先评估它,但在这种情况下这没有意义,因为cons(3, 4)返回一个函数,其中参数 3 和 4 是“集成的”,所以没有办法挑选出参数。

根据我上面给出的解释,从返回的函数conscdr. 现在所cdr要做的就是提供一个映射函数f并返回任何f返回值。

在我看来,car(f)返回一个函数,那么如何cons(3, 4)返回3编辑:错字,应该car(cons(3, 4))代替cons(3, 4)

car(f)不一定返回函数。请参阅上面的类型签名。它只返回返回的任何内容f,如果它恰好是一个函数,那么它将返回一个函数。

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

我希望这能解决问题。

于 2020-09-25T03:06:47.360 回答
1

a(b()),b始终首先被评估。我不知道 Python 中有一个例外。a需要是一个宏才能反过来成立。

注意cdrcar的参数的名称是:f,如“function”中。每个函数都接受一个函数作为参数。但是,这可行,因为正如您所指出的,它cons返回一个函数。

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


cons(3, 4)不返回,确实。返回一个作用于给它的数据的函数。在这种情况下,的函数最终会丢弃第二个传递的参数 ( ),而是返回第一个 ( )。3car(cons(3,4))cons(3, 4)carpair43


是的,暂时远离这样的代码。传递函数非常有用,但这段代码更像是一个实验玩具。这是显示样式的理论代码(源自基于术语的类似 Scheme 的 lisp)。有许多更简单的方法可以实现相同的最终结果。

练习高阶函数的简单示例(如mapreduce),精通它们,然后重新审视这段代码。它仍然很难理解(因为这段代码并不容易理解),但它会更有意义。

于 2020-09-24T19:11:18.233 回答
0

您粘贴的代码是这样使用的:

首先,您定义您的配对:

f = cons(3, 4)

之后,您定义了一个适用于对的函数:

add = lambda x, y: x + y

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

f(add)

输出:

7

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

编辑:

如果您不熟悉lambda表达式,请参阅本教程

现在,你也可以选择

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

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

于 2020-09-24T19:07:47.137 回答