问题描述
有这样的问题:
cons(a,b)
构造一对,car(pair)
和cdr(pair)
返回 该对的第一个和最后一个元素。例如,car(cons(3,4))
返回3
,而cdr(cons(3,4))
返回4
。
现在,我已经看到了解决方案,但是我想知道是否有人可以解释如何思考以达到解决方案?
-
cdr(cons(3,4))
:这两个函数的评估顺序是什么?我通常会认为首先对cons(3,4)
求值,但是在这种情况下这没有意义,因为cons(3,4)
返回的函数将参数3和4进行了“集成”,因此没有任何方法排除争论。 - 在我看来
car(f)
返回一个函数,那么cons(3,4)
如何返回3
? 编辑:错字应为car(cons(3,4))
,而不是cons(3,4)
- 我显然想解决此问题,因为我想学习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
必须是一个宏,相反的情况才能成立。
请注意cdr
和car
的参数名称是:f
,如“函数”中所示。每个函数都接受一个函数作为参数。不过,这是可行的,因为您注意到cons
返回了一个函数。
在car(cons(3,4))
中,cons
返回一个函数(本地称为pair
)。然后将该功能提供给car
,car
在这里使用它:f(pair)
。在这种情况下,f
是传入的函数。此处的复杂部分是f
是接受另一个函数并使用两个参数调用它的函数。这两个参数是最初提供给cons
的数据:3
和4
。
cons(3,4)
不会返回3
,而car(cons(3,4))
会返回cons(3,4)
。 car
返回一个函数,该函数对提供给它的数据起作用。在这种情况下,pair
的{{1}}函数最终会丢弃第二个传递的参数(4
),而是返回第一个传递的参数(3
)。
是的,暂时远离这样的代码一段时间。传递函数非常有用,但是此代码更像是一种实验玩具。这是一种显示样式的理论代码(基于术语,源自诸如Scheme之类的Lisp)。有很多简单的方法可以达到相同的最终结果。
实践Higher Order Functions的简单示例(例如map
和reduce
),使他们精通它们,然后重新阅读此代码。仍然很难理解(因为该代码本身不易于理解),但是更有意义。
您显示的问题也可以通过这种方式解决。
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
是一个带有两个参数a
和b
的函数,然后它返回一个函数f
,该函数带有另一个在给定参数({{ 1}},a
)返回b
,其中c
可以是c
或a
或其他。 b
然后返回f
的值。
好吃!
另一种思考方式是c
返回的函数f
(((a,b) -> c) -> c
)用于将cons
和a
转发给任何运算符(或映射功能)要作用于b
上。该运算符返回cons
。 c
然后简单返回此映射函数返回的值,恰好是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
。
注意a
和cdr
的输入与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
。现在,此3
是3
的结果。
我希望一切都可以清除。
,您粘贴的代码应按以下方式使用:
首先,您定义您的对:
f = cons(3,4)
然后,您定义一个可成对使用的函数:
add = lambda x,y: x + y
现在,您可以像这样使用“对”:
f(add)
输出:
7
因此,它的作用是:将对转换为一个函数,该函数可以对以定义的对作为参数的对“执行”函数。 car
和cds
实际上可以“转换”成对函数并返回一个元素。
如果您对lambda
表达式不熟悉,请参阅this tutorial。
现在,您也可以选择
def add(x,y):
return x + y
并以相同的方式使用它。 :)