[英]Python: functional programming with cons, car & cdr
There is this problem that goes like this:有这个问题是这样的:
cons(a, b)
constructs a pair, andcar(pair)
andcdr(pair)
returns the first and last element of that pair.cons(a, b)
构造一对,car(pair)
和cdr(pair)
返回该cdr(pair)
的第一个和最后一个元素。 For example,car(cons(3, 4))
returns3
, andcdr(cons(3, 4))
returns4
.例如,car(cons(3, 4))
返回3
,cdr(cons(3, 4))
返回4
。
Now, I have seen the solution, but I'm wondering if anybody can explain how to actually think to reach the solution?现在,我已经看到了解决方案,但我想知道是否有人可以解释如何实际思考以达到解决方案?
cdr(cons(3, 4))
: in what order are these two functions evaluated? cdr(cons(3, 4))
:这两个函数的计算顺序是什么? I would normally think that cons(3, 4)
are evaluated first, but that does not make sense in this case, because cons(3, 4)
returns a function where arguments 3 and 4 are "integrated", so there is no way of singling out the arguments.我通常认为cons(3, 4)
首先被评估,但在这种情况下这没有意义,因为cons(3, 4)
返回一个函数,其中参数 3 和 4 是“集成”的,所以没有办法挑出论据。car(f)
returns a function, so how can cons(3, 4)
return 3
?在我看来car(f)
返回一个函数,那么cons(3, 4)
怎么能返回3
呢? EDIT : typo, should be car(cons(3, 4))
instead of cons(3, 4)
编辑:错字,应该是car(cons(3, 4))
而不是cons(3, 4)
Solution:解决方案:
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
In a(b())
, b
will always be evaluated first.在a(b())
, b
总是首先被评估。 I do not know of a single exception to this in Python.我不知道 Python 中有任何例外。 a
would need to be a macro for the reverse to be true. a
需要是一个宏才能使反向为真。
Note what the name of cdr
and car
's parameters are: f
, as in "function".请注意cdr
和car
的参数名称是什么: f
,如“function”。 Each function accepts a function as an argument.每个函数都接受一个函数作为参数。 This works though because, as you noted, cons
returns a function.但这有效,因为正如您所指出的, cons
返回一个函数。
In car(cons(3,4))
, cons
returns a function (locally known as pair
).在car(cons(3,4))
, cons
返回一个函数(本地称为pair
)。 That function is then given to car
, and car
uses it here: f(pair)
.然后将该函数提供给car
, car
在这里使用它: f(pair)
。 In this case, f
is the function that was passed in. The complicated part here is that f
is a function that accepts another function, and calls it with two arguments.在这种情况下, f
是传入的函数。这里复杂的部分是f
是一个函数,它接受另一个函数,并使用两个参数调用它。 Those two arguments are the data that was given to cons
originally: 3
and 4
.这两个参数是最初提供给cons
的数据: 3
和4
。
cons(3, 4)
does not return 3
, car(cons(3,4))
does. cons(3, 4)
不返回3
, car(cons(3,4))
一样。 cons(3, 4)
returns a function that acts on the data that was given to it. cons(3, 4)
返回一个函数,该函数作用于提供给它的数据。 In this case, car
's pair
function ends up throwing away the second passed argument ( 4
), and instead returns the first ( 3
).在这种情况下, car
的pair
函数最终丢弃了第二个传递的参数 ( 4
),而是返回第一个 ( 3
)。
Yes, stay far away from code like this for a while.是的,暂时远离这样的代码。 Passing functions around is quite useful, but this code is more of an experimental toy.传递函数非常有用,但这段代码更像是一个实验玩具。 It's theoretical code to show a style (derived from a lisp like Scheme based on the terminology).它是显示样式的理论代码(基于术语从类似于 Scheme 的 lisp 派生而来)。 There are many, simpler ways of achieving the same end result.有许多更简单的方法可以实现相同的最终结果。
Practice simple examples of Higher Order Functions (like map
and reduce
), become proficient with them, then revisit this code.练习高阶函数的简单示例(如map
和reduce
),熟练使用它们,然后重新访问此代码。 It will still be difficult to comprehend (because, this code doesn't lend itself to easy comprehension), but it will make more sense.理解起来仍然很困难(因为,这段代码并不容易理解),但它会更有意义。
The problem you have shown can also be solved in this way.您所展示的问题也可以通过这种方式解决。
def cons(a, b):
return (a,b)
def car(pair):
return pair[0]
def cdr(pair):
return pair[1]
This is how you will use it:这是您将如何使用它:
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
I'm only showing this so that you can see that there is more than one way to solve that problem, and the way you discovered is very rarely done in python.我只是展示这一点,以便您可以看到解决该问题的方法不止一种,而您发现的方法很少在 python 中完成。 Anyone thinking to solve this in python will 99% of the time do it the way I've shown here.任何想在 python 中解决这个问题的人都会有 99% 的时间按照我在这里展示的方式来做。
Now on to your problem.现在解决你的问题。
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)
First let's talk about these functions using some haskell function notation so that you can see the full type of these functions:首先让我们使用一些 haskell 函数符号来讨论这些函数,以便您可以看到这些函数的完整类型:
cons::(a, b) -> (((a, b) -> c) -> c)
cons
is a function that takes two parameters a
, and b
, then it returns a function f
which takes another function which when given the parameters ( a
, b
), returns c
, where c
could be a
or b
, or something else. cons
是一个函数,它接受两个参数a
和b
,然后它返回一个函数f
,该函数接受另一个函数,当给定参数( a
, b
)时,返回c
,其中c
可以是a
或b
,或其他东西。 f
then returns the value of c
. f
然后返回c
的值。
What a mouthful!多嘴啊!
Another way to think of it is that the function f
( ((a, b) -> c) -> c
) returned by cons
is used to forward a
and b
to any operator (or mapping function) which wants to act on a cons
.另一种思考方式是cons
返回的函数f
( ((a, b) -> c) -> c
) 用于将a
和b
转发给任何想要对b
进行操作的运算符(或映射函数) cons
。 This operator returns c
.此运算符返回c
。 f
then simple returns whatever this mapping function returns, which happens to be c
. f
then simple 返回此映射函数返回的任何内容,恰好是c
。
For now don't worry what c
is.现在不要担心c
是什么。 Just think of it the result of applying a function to a cons
.只需将其cons
将函数应用于cons
。
car::(((a, b) -> a) -> a) -> a
car
defines a possible mapping from (a,b)
to c
, and returns the value of calling f
with this mapping. car
定义了从(a,b)
到c
的可能映射,并返回使用此映射调用f
的值。
car
takes a function f
that wants a mapping from the input (a,b)
to some output c
. car
接受一个函数f
,它需要从输入(a,b)
到某个输出c
的映射。 In this case, car
defines the mapping as (a, b) -> a
which means any function f
which is passed to car
will return the first argument of (a,b)
, which is just a
.在这种情况下, car
将映射定义为(a, b) -> a
这意味着传递给car
任何函数f
都将返回(a,b)
的第一个参数,它只是a
。 And that is what car
will return.这就是car
将返回的。
cdr::(((a, b) -> b) -> b) -> b
Similar to car
, but the mapping defined by cdr
returns b
instead of a
.类似于car
,但cdr
定义的映射返回b
而不是a
。
Notice how similar the input for cdr
and car
are to the function ( f
) returned by cons
?请注意cdr
和car
的输入与cons
返回的函数 ( f
) 有多相似? This is why I just called their inputs f
这就是为什么我只是称他们的输入f
Now to answer some of your questions:现在回答你的一些问题:
cdr(cons(3, 4))
: in what order are these two functions evaluated?cdr(cons(3, 4))
:这两个函数的计算顺序是什么? I would normally think thatcons(3, 4)
are evaluated first, but that does not make sense in this case, becausecons(3, 4)
returns a function where arguments 3 and 4 are "integrated", so there is no way of singling out the arguments.我通常认为cons(3, 4)
首先被评估,但在这种情况下这没有意义,因为cons(3, 4)
返回一个函数,其中参数 3 和 4 是“集成”的,所以没有办法挑出论据。
In light of the explanation I gave above, the function returned from cons
is exactly the same type as the one expected by cdr
.根据我上面给出的解释,从cons
返回的函数与cdr
期望的函数类型完全相同。 Now all cdr
has to do is supply a mapping function to f
and return whatever f
returns.现在cdr
要做的就是为f
提供一个映射函数并返回f
返回的任何内容。
It seems to me that
car(f)
returns a function, so how cancons(3, 4)
return3
?在我看来car(f)
返回一个函数,那么cons(3, 4)
怎么能返回3
呢? EDIT : typo, should becar(cons(3, 4))
instead ofcons(3, 4)
编辑:错字,应该是car(cons(3, 4))
而不是cons(3, 4)
car(f)
does not necessarily return a function. car(f)
不一定返回函数。 See the type signatures above.请参阅上面的类型签名。 It just returns whatever f
returns and if that happens to be a function, then that's what it will return a function.它只返回f
返回的任何内容,如果它恰好是一个函数,那么它将返回一个函数。
In general, car
returns the first element of a cons
.通常, car
返回cons
的第一个元素。 In this case, since cons(3,4)
returns a function ( f
) and this function is passed to car
, then car
will supply this function with another function that chooses the first of it's arguments, which is 3
in this case.在这种情况下,由于cons(3,4)
返回一个函数 ( f
) 并且该函数被传递给car
,因此car
将为该函数提供另一个函数,该函数选择它的第一个参数,在这种情况下为3
。 This 3
is now the result of car(cons(3,4)
.这个3
现在是car(cons(3,4)
。
I hope that clears things up.我希望这能解决问题。
The code You pasted is meant to be used like this:您粘贴的代码旨在这样使用:
First, You define Your pair:首先,您定义您的配对:
f = cons(3, 4)
After that, You define a function that works on pairs:之后,您定义一个对对起作用的函数:
add = lambda x, y: x + y
Now, You can use Your "pair" like this:现在,您可以像这样使用您的“配对”:
f(add)
output:输出:
7
So, what it does is: It converts Your pair into a function that can "execute" functions on pairs with the defined pair as arguments.所以,它的作用是:它将您的对转换为一个函数,该函数可以在对以定义的对作为参数的对上“执行”函数。 car
and cds
can actually "convert" your pair-function back and return an element. car
和cds
实际上可以将您的配对函数“转换”回来并返回一个元素。
In case You are not familiar to lambda
expressions, see this tutorial .如果您不熟悉lambda
表达式,请参阅本教程。
For now, You could also go with现在,你也可以去
def add(x, y): return x + y
and use it just the same way.并以同样的方式使用它。 :) :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.