简体   繁体   English

Python:带有 cons、car 和 cdr 的函数式编程

[英]Python: functional programming with cons, car & cdr

There is this problem that goes like this:有这个问题是这样的:

cons(a, b) constructs a pair, and car(pair) and cdr(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)) returns 3 , and cdr(cons(3, 4)) returns 4 .例如, car(cons(3, 4))返回3cdr(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?现在,我已经看到了解决方案,但我想知道是否有人可以解释如何实际思考以达到解决方案?

  1. 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 是“集成”的,所以没有办法挑出论据。
  2. It seems to me that 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)
  3. I am obviously trying to solve this problem because I want to learn Python, but would you recommend me to skip these problems?我显然是想解决这个问题,因为我想学习 Python,但是你会建议我跳过这些问题吗? I am tempted to do so by reading here: Why program functionally in Python?我很想通过阅读这里来做到这一点: 为什么用 Python 进行功能性编程?

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".请注意cdrcar的参数名称是什么: 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) .然后将该函数提供给carcar在这里使用它: 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的数据: 34


cons(3, 4) does not return 3 , car(cons(3,4)) does. cons(3, 4)返回3car(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 ).在这种情况下, carpair函数最终丢弃了第二个传递的参数 ( 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.练习高阶函数的简单示例(如mapreduce ),熟练使用它们,然后重新访问此代码。 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)))

Output:输出:

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是一个函数,它接受两个参数ab ,然后它返回一个函数f ,该函数接受另一个函数,当给定参数( ab )时,返回c ,其中c可以是ab ,或其他东西。 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 ) 用于将ab转发给任何想要对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 ?请注意cdrcar的输入与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 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 是“集成”的,所以没有办法挑出论据。

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 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)

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. carcds实际上可以将您的配对函数“转换”回来并返回一个元素。

Edit: 编辑:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM