精华内容
下载资源
问答
  • 在前面内容中我们调用了一标准C的库函数,叫printf,那么如果我们想自己定义函数应该如何来编写程序呢?定义函数又有什么好处呢?因为我们在教材中提及到的例子主要目的是为了让读者对程序的原理有一定的了解,...

    在前面内容中我们调用了一个标准C的库函数,叫printf,那么如果我们想自己定义函数应该如何来编写程序呢?定义函数又有什么好处呢?因为我们在教材中提及到的例子主要目的是为了让读者对程序的原理有一定的了解,所以设定的例子程序通常都比较简单,基本上在几行到十几行,多一点的也就三五十行代而已,但是在真正的编程工作中,我们需要完成的代码将非常大,所以将代码合理的分为不同的区块是很有必要的,每一个区块具有相对独立的功能,并为其它程序提供对外调用的参数和返回值,这样由多个区域组成的程序才会让程序阅读者更方便的理解程序设计的理念。并可以通过函数让功能被封装起来,使得一个功能可以在不同的情况下被其它功能调用,函数的概念就是这样产生的。我们来看看函数的定义与使用。

    一、定义函数

    定义一个函数我们需要确定三部分内容:

    1.函数的返回值类型

    2.函数的名称

    3.函数的参数

    确定了以上内容后就可以定义一个特有功能的函数了:

    int wrongplus(int a, int b)

    {

    a = a + 1;

    b = b + 1;

    return a + b;

    }

    例如上面代码中就定义了一个返回值为int类型的函数,其函数名为wrongplus,一对小括号中的内容为这个函数的参数,也就是说这个函数有两个传入参数int a和int b。在函数体内,分别让a在原来的基础上加1,让b在原来的基础上加1,然后使用return关键字返回a + b的和。如果a的值是2,而b的值是3,那么这个函数的返回值则是7。

    如果我们想定义一个没有返回值类型的函数则需要设计其返回值类型为void,同样,如果我们想定义一个没有参数的函数可以将其参数定义为void,通常如果一个函数没有参数可以在函数名后的小括号里什么都不写,例如:

    void function1(void)

    {

    }

    void function2()

    {

    }

    二、函数调用与声明

    我们定义了这样一个wrongplus()的函数,那么如何来让其它功能使用它呢?我们看下面程序:

    #include main()

    {

    int a = 2, b = 3;

    int c;

    int wrongplus(int a, int b);

    c = wrongplus(a, b);

    printf("%d + %d = %d\n",a, b, c);

    }

    int wrongplus(int a, int b)

    {

    a = a + 1;

    b = b + 1;

    return a + b;

    }

    2 + 3 = 7

    如果函数的定义是在调用函数的后面(下面)则需要在调用之前声明函数的定义,否则不需要事先声明。声明的意思是告诉编译器,这个函数的返回值类型、函数名和参数。

    我们在调用一个函数时,需要向这个函数传其需要的参数,例如a和b这两个变量,并可以指定另一个变量用于接收这个函数执行后的返回值,比如变量c就接收了函数wrongplus()的返回值,结果为7。但是我们看到程序的运行结果是2 + 3 = 7,也就是说,执行了函数wrongplus()后,我们虽然对函数内的参数变量分别加1,但主函数main中的a和b的值并没有发生变化。

    三、实参与形参

    当我们调用函数时,对其传入的参数和函数体内接收到的参数其实并不是同一个变量。调用函数时向函数传入的参数称作实际参数,简称实参,而函数体内部用于接收外部调用时传入的参数的参数称为形式参数,简称形参。

    例如上面例子中,在main函数中定义的变量int a和int b,在调用wrongplus(a, b);时这两变量是实参,也就是将这两个变量的值2和3传入到函数内部。而在函数wrongplus()的函数体内,用于接收这两个参数时的int a和int b则是形参,这是函数体内部额外的两个变量,用于接收2和3这两个值,这两个变量并不是main函数中定义的a和b 。

    在wrongplus()函数体内,对形参做a = a + 1;和b = b + 1;操作,其实只影响到了函数体内部的这两个变量,而并未影响到main函数中的变量a和b的值。所以,在wrongplus()函数体内的a和b的值为3和4,函数运行结果的返回值为7,而在main函数中的a和b两变量的值仍然是2和3,所以printf()函数显示出的结果则是 2 + 3 = 7 。

    欢迎关注公众号:编程外星人

    展开全文
  • python函数定义函数 重点 (Top highlight)No matter what implementation mechanisms programming languages use, all of them have a reserved seat for functions. Functions are essential parts of any code ...

    python函数内定义函数

    重点 (Top highlight)

    No matter what implementation mechanisms programming languages use, all of them have a reserved seat for functions. Functions are essential parts of any code project because they’re responsible for preparing and processing data and configuring user interface elements. Without exception, Python, while positioned as an object-oriented programming language, depends on functions to perform data-related operations. So, writing good functions is critical to building a resilient code base.

    不管编程语言使用哪种实现机制,它们都为功能保留了席位。 功能是任何代码项目中必不可少的部分,因为它们负责准备和处理数据以及配置用户界面元素。 毫无疑问,Python被定位为面向对象的编程语言,它依赖于函数来执行与数据相关的操作。 因此,编写良好的功能对于构建弹性代码库至关重要。

    It’s straightforward to define a few simple functions in a small project. With the growth of the project scope, the functions can get far more complicated and the need for more functions grows exponentially. Getting all the functions to work together without any confusion can be a headache, even to experienced programmers. Applying best practices to function declarations becomes more important as the scope of your project grows. In this article, I’d like to talk about best practices for declaring functions — knowledge I have accrued over years of coding.

    在一个小项目中定义一些简单的函数很简单。 随着项目范围的扩大,功能可能变得更加复杂,对更多功能的需求也呈指数增长。 即使没有经验的程序员,要想将所有功能完美地结合在一起也是一件令人头疼的事情。 随着项目范围的扩大,将最佳实践应用于函数声明变得越来越重要。 在本文中,我想谈一谈声明函数的最佳实践,这是我经过多年编码积累的知识。

    1.一般准则 (1. General Guidelines)

    You may be familiar with these general guidelines, but I’d like to discuss them first because they’re high-level, good practices that many programmers don’t appreciate. When developers don’t follow these guidelines, they pay the price — the code is very hard to maintain.

    您可能熟悉这些通用准则,但我想首先讨论它们,因为它们是许多程序员不赞赏的高级高级实践。 当开发人员不遵循这些准则时,他们会付出代价-代码很难维护。

    显式和有意义的名称 (Explicit and meaningful names)

    We have to give meaningful names to our functions. As you know, functions are also objects in Python, so when we define a function, we basically create a variable of the function type. So, the variable name (i.e. the name of the function) has to reflect the operation it performs.

    我们必须给我们的功能起有意义的名字。 如您所知,函数也是Python中的对象,因此在定义函数时,我们基本上会创建函数类型的变量。 因此,变量名称(即函数的名称)必须反映其执行的操作。

    Although readability has become more emphasized in modern coding, it’s mostly talked about in regards to comments — it’s much less often discussed in relation to code itself. So, if you have to write extensive comments to explain your functions, it’s very likely that your functions don’t have good names. Don’t worry about having a long function name — almost all modern IDEs have excellent auto-completion hints, which will save you from typing the entire long names.

    尽管在现代编码中可读性已得到越来越多的重视,但在注释方面却经常被谈论,而与代码本身有关的讨论则少得多。 因此,如果您必须写大量注释来解释您的功能,则您的功能很可能没有好名字。 不必担心函数名很长-几乎所有现代IDE都具有出色的自动完成提示,这将使您不必键入整个长名。

    # Too generic, wanting others to guess what it does??
    def foo(): pass
    
    
    # Lack of details, requiring contexts to understand
    def do_step1(): pass
    
    
    # Not following naming conventions, which should be snake style and all lowercase
    def GETData(): pass
    
    
    # A few explicit and meaningful names
    def get_account_info(): pass
    
    
    def generate_sales_report(): pass

    Good naming rules should also apply to the arguments of the function and all local variables within the function. Something else to note is that if your functions are intended to be used within your class or module, you may want to prefix the name with an underscore (e.g., def _internal_fun():) to indicate that these functions are for private usages and they’re not public APIs.

    好的命名规则也应适用于函数的参数以及函数内的所有局部变量。 还有一点需要注意的是,如果打算在类或模块中使用您的函数,则可能要在名称前加上下划线(例如def _internal_fun():以表明这些函数供私人使用,它们不是公开的API。

    小型单用途 (Small and Single Purpose)

    Your functions should be kept small, so they’re easier to manage. Imagine that you’re building a house (not a mansion). However, the bricks you’re using are one meter cubed. Are they easy to use? Probably not — they’re too large. The same principle applies to functions. The functions are the bricks of your project. If the functions are all enormous in size, your construction won’t progress as smoothly as it could. When they’re small, they’re easier to fit into various places and moved around if the need arises.

    您的功能应保持较小,以便于管理。 想象一下,您正在盖房子(不是豪宅)。 但是,您使用的砖是一米的立方。 它们易于使用吗? 可能不是-它们太大了。 相同的原理适用于功能。 功能是项目的基础。 如果功能全部庞大,那么您的构建将无法顺利进行。 当它们很小时,它们更容易放入各种地方并在需要时四处移动。

    It’s also key for your functions to serve single purposes, which can help you keep your functions small. Another benefit of single-purpose functions is that you’ll find it much easier to name such functions. You can simply name your function based on its intended single purpose. The following is how we can refactor our functions to make each of them serve only one purpose each. Another thing to note is that by doing that, you can minimize the comments that you need to write — because all the function names tell the story.

    这也是功能实现单一目的的关键,这可以帮助您使功能保持较小。 单一功能的另一个好处是,您会发现命名此类功能要容易得多。 您可以简单地根据其预期的单一用途来命名函数。 以下是我们如何重构我们的功能,以使每个功能仅用于一个目的。 要注意的另一件事是,这样做可以最大限度地减少需要编写的注释,因为所有函数名都可以说明问题。

    # Embed all operations with one single function
    def process_data():
        # a bunch of code to read data
        # a bunch of code to clean the data
        # a bunch of code to generate the report
        pass
    
    
    # Refactor by create additional smaller functions
    def read_data_from_path(filepath):
        return data
    
    
    def clean_data(data): 
        return cleaned_data
    
    
    def generate_report(cleaned_data):
        return report
    
    
    def process_data():
        data = read_data_from_path(filepath="path_to_file.csv")
        cleaned_data = clean_data(data)
        report = generate_report(cleaned_data)
        return report

    不要重新发明轮子 (Don’t reinvent the wheel)

    You don’t have unlimited energy and time to write functions for every operation you need, so it’s essential to be familiar with common functions in standard libraries. Before you define your own functions, think about whether the particular business need is common — if so, it’s likely that these particular and related needs have already been addressed.

    您没有无限的精力和时间来编写所需的每项操作的函数,因此熟悉标准库中的常见函数至关重要。 在定义自己的功能之前,请考虑一下特定的业务需求是否是通用的—如果是这样,则可能已经解决了这些特定的需求和相关需求。

    For instance, if you work with data in the CSV format, you can look into the functionalities in the CSV module. Alternatively, the pandas library can handle CSV files gracefully. For another instance, if you want to count elements in a list, you should consider the Counter class in the collections module, which is designed specifically for these operations.

    例如,如果您使用CSV格式的数据,则可以查看CSV模块中的功能。 另外, pandas库可以优雅地处理CSV文件。 对于另一个实例,如果要对列表中的元素进行计数,则应考虑collections模块中的Counter类,该类专门针对这些操作而设计。

    2.默认参数 (2. Default Arguments)

    相关场景 (Relevant scenarios)

    When we first define a function, it usually serves one particular purpose. However, when you add more features to your project, you may realize that some closely related functions can be merged. The only difference is that the invocation of the merged function sometimes involves passing another argument or setting slightly different arguments. In this case, you can consider setting a default value to the argument.

    当我们第一次定义一个函数时,它通常有一个特定的用途。 但是,当您向项目中添加更多功能时,您可能会意识到可以合并一些紧密相关的功能。 唯一的区别是合并函数的调用有时涉及传递另一个参数或设置略有不同的参数。 在这种情况下,您可以考虑为参数设置默认值。

    The other common scenario is that when you declare a function, you already expect that your function serves multiple purposes, with function calls using differential parameters while some other parameters requiring few variations. You should consider setting a default value to the less varied argument.

    另一个常见的情况是,在声明一个函数时,您已经期望该函数有多种用途,其中函数调用使用差分参数,而其他一些参数几乎不需要变化。 您应该考虑为较少变化的参数设置默认值。

    设置默认参数 (Set default arguments)

    The benefit of setting default arguments is straightforward — you don’t need to deal with setting unnecessary arguments in most cases. However, the availability of keeping these parameters in your function signature allows you to use your functions more flexibly when you need to. For instance, for the built-in sorted() function, there are several ways to call the function, but in most cases, we just use the basic form: sorted(the_iterable), which will sort the iterable in the ascending lexicographic order. However, when you want to change the ascending order or the default lexicographic order, we can override the default setting by specifying the reverse and key arguments.

    设置默认参数的好处很简单-在大多数情况下,您无需处理设置不必要的参数。 但是,将这些参数保留在函数签名中的可用性使您可以在需要时更灵活地使用函数。 例如,对于内置的sorted()函数,有几种方法可以调用该函数,但在大多数情况下,我们仅使用基本形式: sorted(the_iterable) ,它将按字典顺序升序对iterable进行排序。 但是,当您想更改升序或默认字典顺序时,我们可以通过指定reversekey参数来覆盖默认设置。

    We should apply the same practice to our own function declaration. In terms of what value we should set, the rule of thumb is you should choose the default value that is to be used for most function calls. Because this is an optional argument, you (or the users of your APIs) don’t want to set it in most situations. Consider the following example:

    我们应该对我们自己的函数声明采用相同的做法。 根据我们应该设置的值,经验法则是您应该选择用于大多数函数调用的默认值。 因为这是一个可选参数,所以您(或您的API用户)在大多数情况下都不想设置它。 考虑以下示例:

    # Set the price to the sale price or clearance sale with has addition discount
    def set_regular_sale_price(price, discount):
        price *= discount
        return price
    
    
    
    
    def set_clearance_sale_price(price, discount, additional_discount):
        sale_price = set_sale_price(price, discount)
        return sale_price * additional_discount
    
    
    
    
    # A refactored combined function
    def set_sale_price(price, discount, additional_discount=1):
        sale_price = price * discount
        return sale_price * additional_discount

    避免易变的默认参数的陷阱 (Avoid the pitfalls of mutable default arguments)

    There is a catch for setting the default argument. If your argument is a mutable object, it’s important that you don’t set it using the default constructor — because functions are objects in Python and they’re created when they’re defined. The side effect is that the default argument is evaluated at the time of function declaration, so a default mutable object is created and becomes part of the function. Whenever you call the function using the default object, you’re essentially accessing the same mutable object associated with the function, although your intention may be having the function to create a brand new object for you. The following code snippet shows you the unwanted side effect of setting a default mutable argument:

    有一个设置默认参数的陷阱。 如果您的参数是一个可变对象,请务必不要使用默认构造函数对其进行设置,这很重要-因为函数是Python中的对象,并且它们是在定义时创建的。 副作用是,在函数声明时对默认参数进行了评估,因此将创建一个默认的可变对象,并成为该函数的一部分。 每当您使用默认对象调用该函数时,实际上您都在访问与该函数关联的相同可变对象,尽管您的意图可能是希望该函数为您创建一个全新的对象。 以下代码段向您展示了设置默认可变参数的有害副作用:

    >>> def add_item_to_cart(new_item, shopper_name, existing_items=[]):
    ...     existing_items.append(new_item)
    ...     print(f"{shopper_name}'s cart has {existing_items}")
    ...     return existing_items
    ... 
    ... 
    ... shopping_list_wife = add_item_to_cart("Dress", "Jennifer")
    ... shopping_list_husband = add_item_to_cart("Soccer", "David")
    ... 
    Jennifer's cart has ['Dress']
    David's cart has ['Dress', 'Soccer']

    As shown above, although we intended to create two distinct shopping lists, the second function call still accessed the same underlying object, which resulted in the Soccer item added to the same list object. To solve the problem, we should use the following implementation. Specifically, you should use None as the default value for a mutable argument:

    如上所示,尽管我们打算创建两个不同的购物清单,但是第二个函数调用仍访问相同的基础对象,这导致将Soccer项目添加到同一清单对象中。 要解决该问题,我们应该使用以下实现。 具体来说,您应该使用None作为可变参数的默认值:

    def add_item_to_cart(new_item, shopper_name, existing_items=None):
        if existing_items is None:
            existing_items = list()
        existing_items.append(new_item)
        print(f"{shopper_name}'s cart has {existing_items}")
        return existing_items

    3.考虑返回多个值 (3. Consider Returning Multiple Values)

    元组中的多个值 (Multiple values in a tuple)

    When your function performs complicated operations, the chances are that these operations can generate two or more objects, all of which are needed for your subsequent data processing. Theoretically, it’s possible that you can create a class to wrap these objects such that your function can return the class instance as its output. However, it’s possible in Python that a function can return multiple values. More precisely speaking, these multiple values are returned as a tuple object. The following code shows you a trivial example:

    当您的函数执行复杂的操作时,这些操作很可能会生成两个或多个对象,所有这些对于后续的数据处理都是必需的。 从理论上讲,可以创建一个类来包装这些对象,以便函数可以将类实例作为其输出返回。 但是,在Python中,函数可能会返回多个值。 更准确地说,这些多个值作为元组对象返回。 以下代码显示了一个简单的示例:

    >>> from statistics import mean, stdev
    ... 
    ... def evaluate_test_result(scores):
    ...     scores_mean = mean(scores)
    ...     scores_std = stdev(scores)
    ...     return scores_mean, scores_std
    ... 
    ... evaluation_result = evaluate_test_result([1, 1, 1, 2, 2, 2, 6, 6, 6])
    ... print(f"Evaluation Result ({type(evaluation_result)}): {evaluation_result}")
    ... 
    Evaluation Result (<class 'tuple'>): (3, 2.29128784747792)

    As shown above, the returned values are simply separated by a comma, which essentially creates a tuple object, as checked by the type() function.

    如上所示,返回值由逗号简单分隔,实际上由type()函数检查它会创建一个元组对象。

    但不超过三个 (But no more than three)

    One thing to note is that although Python functions can return multiple values, you should not abuse this feature. One value (when a function doesn’t explicitly return anything, it actually returns None implicitly) is best — because everything is straightforward and most users usually expect a function to return only one value. In some cases, returning two values is fine, returning three values is probably still OK, but please don’t ever return four values. It can create a lot of confusion for the users over which are which. If it happens, this is a good indication that you should refactor your functions — your functions probably serve multiple purposes and you should create smaller ones with more dedicated responsibilities.

    需要注意的一件事是,尽管Python函数可以返回多个值,但您不应滥用此功能。 一个值(当一个函数不显式返回任何值时,它实际上隐式返回None )是最好的-因为一切都很简单,而且大多数用户通常希望函数仅返回一个值。 在某些情况下,返回两个值很好,返回三个值可能仍然可以,但是请不要返回四个值。 它会给哪个用户带来很多混乱。 如果发生这种情况,这很好地表明您应该重构您的功能-您的功能可能有多种用途,您应该创建较小的功能并承担更多责任。

    4.使用Try…除外 (4. Use Try…Except)

    When you define functions as public APIs, you can’t always assume that the users set the desired parameters to the functions. Even if we use the functions ourselves, it’s possible that some parameters are created out of our control and they’re incompatible with our functions. In these cases, what should we do in our function declaration?

    在将函数定义为公共API时,不能总是假定用户为函数设置了所需的参数。 即使我们自己使用函数,也有可能某些参数是在我们的控制范围之外创建的,并且与我们的函数不兼容。 在这些情况下,我们应该在函数声明中做什么?

    The first consideration is to use the try…except statement, which is the typical exception handling technique. You embed the code that can possibly go wrong (i.e., raise certain exceptions) in the try clause and the possible exceptions are handled in the except clause.

    首先要考虑的是使用try…except语句,这是典型的异常处理技术。 您将可能会出错(即引发某些异常)的代码嵌入try子句中,并且可能的异常在except子句中进行处理。

    Let’s consider the following scenario. Suppose that the particular business need is that your function takes a file path and if the file exists and is read successfully, your function does some data processing operations with the file and returns the result, otherwise returns -1. There are multiple ways to implement this need. The code below shows you a possible solution:

    让我们考虑以下情形。 假设特定的业务需求是您的函数采用文件路径,并且如果文件存在并且被成功读取,则您的函数会对文件进行一些数据处理操作并返回结果,否则返回-1 。 有多种方法可以实现此需求。 下面的代码为您展示了一种可能的解决方案:

    def get_data_from_file(filepath):
        try:
            with open(filepath) as file:
                computed_value = process_data(file)
        except Exception:
            return -1
        else:
            return computed_value
    
    
    def process_data(file):
        # process the data
        return computed_value

    In other words, if you expect that users of your functions can set some arguments that result in exceptions in your code, you can define functions that handle these possible exceptions. However, this should be communicated with the users clearly, unless it’s part of the feature as shown in the example (return -1 when the file can’t be read).

    换句话说,如果您期望函数的用户可以设置一些导致代码中出现异常的参数,则可以定义处理这些可能异常的函数。 但是,应该清楚地与用户沟通,除非它是示例中所示的功能的一部分(当无法读取文件时返回-1 )。

    5.考虑参数验证 (5. Consider Argument Validation)

    The previous function using the try…except statement is sometimes referred to as the EAFP (Easier to Ask Forgiveness than Permission) coding style. There is another coding style called LBYL (Look Before You Leap), which stresses the sanity check before running particular code blocks.

    以前使用try…except语句的函数有时被称为EAFP(比许可要求更容易获得宽恕)编码风格。 还有另一种称为LBYL的编码样式(请先了解一下),该样式强调在运行特定代码块之前进行的完整性检查。

    Following the previous example, in terms of applying LBYL to function declaration, the other consideration is to validate your function’s arguments. One common use case for argument validation is to check whether the argument is of the right data type. As we all know, Python is a dynamically-typed language, which doesn’t enforce type checking. For instance, your function’s arguments should be integers or floating-point numbers. However, calling the function by setting strings — the invocation itself — won’t prompt any error messages until the function is executed.

    在前面的示例之后,就将LBYL应用于函数声明而言,另一个考虑因素是验证函数的参数。 参数验证的一种常见用例是检查参数是否具有正确的数据类型。 众所周知,Python是一种动态类型的语言,它不执行类型检查。 例如,函数的参数应为整数或浮点数。 但是,通过设置字符串(调用本身)来调用函数,直到函数执行后才会提示任何错误消息。

    The following code shows how to validate the arguments before running the code:

    以下代码显示了在运行代码之前如何验证参数:

    # Check type before running the code
    def add_numbers(a, b):
        if not(isinstance(a, (float, int)) and isinstance(b, (float, int))):
            raise TypeError("Numbers are required.")
        return a + b

    讨论:EAFP与LBYL (Discussion: EAFP vs. LBYL)

    It should be noted that both EAFP and LBYL can be applied to more than just dealing with function arguments. They can be applied anywhere in your functions. Although EAFP is a preferred coding style in the Python world, depending on your use case, you should also consider using LBYL which can provide more user-friendly function-specific error messages than the generic built-in error messages you get with the EAFP style.

    应该注意的是,EAFP和LBYL不仅可以用于处理函数参数,还可以应用于其他方面。 它们可以应用于您的函数中的任何位置。 尽管EAFP在Python世界中是首选的编码样式,但是根据您的用例,您还应该考虑使用LBYL,它可以提供比EAFP样式所提供的通用内置错误消息更多的用户友好的特定于函数的错误消息。 。

    6.将Lambda函数视为替代方法 (6. Consider Lambda Functions As Alternatives)

    作为其他功能的参数 (Functions as parameters of other functions)

    Some functions can take another function (or are callable, in general terms) to perform particular operations. For instance, the sorted() function has the key argument that allows us to define more custom sorting behaviors. The following code snippet shows you a use case:

    一些功能可以采用其他功能(或一般而言可调用)来执行特定操作。 例如, sorted()函数具有key参数,该参数允许我们定义更多自定义排序行为。 以下代码段显示了一个用例:

    >>> # A list of dictionary objects for sorting
    >>> grades = [{'name': 'John', 'score': 97},
    ...           {'name': 'David', 'score': 96},
    ...           {'name': 'Jennifer', 'score': 98},
    ...           {'name': 'Ashley', 'score': 94}]
    >>> def sorting_grade(x):
    ...     return x['score']
    ... 
    >>> sorted(grades, key=sorting_grade)
    [{'name': 'Ashley', 'score': 94}, {'name': 'David', 'score': 96}, {'name': 'John', 'score': 97}, {'name': 'Jennifer', 'score': 98}]

    Lambda可以替代 (Lambda functions as alternatives)

    Notably, the sorting_grade function was used just once and it’s a simple function — in which case, we can consider using a lambda function.

    值得注意的是, sorting_grade函数仅使用了一次,这是一个简单的函数-在这种情况下,我们可以考虑使用lambda函数。

    If you’re not familiar with the lambda function, here’s a brief description. A lambda function is an anonymous function declared using the lambda keyword. It takes zero to more arguments and has one expression for applicable operations with the form: lambda arguments: expression. The following code shows you how we can use a lambda function in the sorted() function, which looks a little cleaner than the solution above:

    如果您不熟悉lambda函数,这里有一个简短的描述。 lambda函数是使用lambda关键字声明的匿名函数。 它需要零到更多的参数,并且具有一个适用于操作的表达式,其形式为: lambda arguments: expression 。 以下代码向您展示了如何在sorted()函数中使用lambda函数,该函数看起来比上述解决方案还干净一些:

    >>> sorted(grades, key=lambda x: x['score'])
    [{'name': 'Ashley', 'score': 94}, {'name': 'David', 'score': 96}, {'name': 'John', 'score': 97}, {'name': 'Jennifer', 'score': 98}]

    Another common use-case that’s relevant to many data scientists is the use of lambda functions when they work with the pandas library. The following code is a trivial example how a lambda function assists data manipulation using the map() function, which operates each item in a pandas Series object:

    与许多数据科学家相关的另一个常见用例是在与熊猫库配合使用时使用lambda函数。 以下代码是一个简单的示例, lambda函数如何使用map()函数辅助数据处理,该函数操作pandas Series对象中的每个项目:

    >>> import pandas as pd
    >>> interest_rates = pd.Series([0.023, 0.025, 0.037])
    >>> interest_rates.map(lambda x: f"{x:.2%}")
    0    2.30%
    1    2.50%
    2    3.70%
    dtype: object

    7.考虑装饰器 (7. Consider Decorators)

    装饰工 (Decorators)

    Decorators are functions that modify the behavior of other functions without affecting their core functionalities. In other words, they provide modifications to the decorated functions at the cosmetic level. If you don’t know too much about decorators, please feel free to refer to my earlier articles (1, 2, and 3). Here’s a trivial example of how decorators work in Python.

    装饰器是在不影响其核心功能的情况下修改其他功能的行为的功能。 换句话说,它们在装饰水平上对装饰功能进行了修改。 如果你不知道太多的装饰,请随时参考我以前的文章( 123 )。 这是装饰器如何在Python中工作的简单示例。

    >>> # Define a decorator function
    ... def echo_wrapper(func):
    ...     def wrapper(*args, **kwargs):
    ...         func(*args, **kwargs)
    ...         func(*args, **kwargs)
    ...     return wrapper
    ... 
    >>> # Define a function that is decorated by echo_wrapper
    ... @echo_wrapper
    ... def say_hello():
    ...     print('Hello!')
    ... 
    >>> # Call the decorated function
    ... say_hello()
    Hello!
    Hello!

    As shown, the decorator function simply runs the decorated function twice. To use the decorator, we simply place the decorator function name above the decorated function with an @ prefix. As you can tell, the decorated function did get called twice.

    如图所示,装饰器函数只需将装饰函数运行两次。 要使用装饰器,我们只需将装饰器函数名称放在带有@前缀的装饰函数上方即可。 如您所知,修饰函数确实被调用了两次。

    在函数声明中使用装饰器 (Use decorators in function declarations)

    For instance, one useful decorator is the property decorator that you can use in your custom class. The following code shows you how it works. In essence, the @property decorator converts an instance method to make it behave like a regular attribute, which allows the access of using the dot notation.

    例如,一种有用的装饰器是可以在自定义类中使用的属性装饰器。 以下代码向您展示了它是如何工作的。 本质上, @property装饰器将转换一个实例方法以使其表现得像常规属性,从而允许使用点表示法进行访问。

    >>> class Product:
    ...     def __init__(self, item_id, price):
    ...         self.item_id = item_id
    ...         self.price = price
    ... 
    ...     @property
    ...     def employee_price(self):
    ...         return self.price * 0.9
    ... 
    >>> product = Product(12345, 100)
    >>> product.employee_price
    90.0

    Another trivial use case of decorators is the time logging decorator, which can be particularly handy when the efficiency of your functions is of concern. The following code shows you such a usage:

    装饰器的另一个琐碎用例是时间记录装饰器,当您关注功能的效率时,它可能特别方便。 以下代码向您展示了这种用法:

    >>> from time import time
    ... 
    ... # Create the decorator function
    ... def logging_time(func):
    ...     def logged(*args, **kwargs):
    ...         start_time = time()
    ...         func(*args, **kwargs)
    ...         elapsed_time = time() - start_time
    ...         print(f"{func.__name__} time elapsed: {elapsed_time:.5f}")
    ... 
    ...     return logged
    ... 
    ... @logging_time
    ... def calculate_integer_sum(n):
    ...     return sum(range(n))
    ... 
    ... @logging_time
    ... def calculate_integer_square_sum(n):
    ...     return sum(x*x for x in range(n))
    ... 
    >>> calculate_integer_sum(10000)
    calculate_integer_sum time elapsed: 0.00027
    >>> calculate_integer_square_sum(10000)
    calculate_integer_square_sum time elapsed: 0.00110

    8.使用* args和** kwargs-但精简 (8. Use *args and **kwargs — But Parsimoniously)

    In the previous section, you saw the use of *args and **kwargs in defining our decorator function, the use of which allows the decorator function to decorate any functions. In essence, we use *args to capture all (or an undetermined number of, to be more general) position arguments while **kwargs to capture all (or an undetermined number of, to be more general) keyword arguments. Specifically, position arguments are based on the positions of the arguments that are passed in the function call, while keyword arguments are based on setting parameters to specifically named function arguments.

    在上一节中,您看到了在定义装饰器函数时使用了*args**kwargs ,使用它们可以使装饰器函数来装饰任何函数。 从本质上讲,我们使用*args捕获所有(或更不确定的数量,更通用)的位置参数,而**kwargs捕获所有(或更不确定的数量,更通用的)关键字参数。 具体而言,位置参数基于在函数调用中传递的参数的位置,而关键字参数基于将参数设置为专门命名的函数参数。

    If you’re unfamiliar with these terminologies, here’s a quick peek to the signature of the built-in sorted() function: sorted(iterable, *, key=None, reverse=False). The iterable argument is a position argument, while the key and reverse arguments are keyword arguments.

    如果您不熟悉这些术语,可以快速查看一下内置sorted()函数的签名: sorted( iterable , * , key=None , reverse=False )iterable参数是位置参数,而keyreverse参数是关键字参数。

    The major benefit of using *args and **kwargs is to make your function declaration looks clean, or less noisy for the same matter. The following example shows you a legitimate use of *arg in function declaration, which allows your function to accept any number of position arguments.

    使用*args**kwargs的主要好处是使您的函数声明看起来很整洁,或在同一情况下噪音较小。 以下示例向您展示了在函数声明中合法使用*arg情况,该声明允许您的函数接受任意数量的位置参数。

    >>> # Define a function that accepts undetermined position arguments
    >>> def stringify(*args):
    ...     return [str(x) for x in args]
    ... 
    >>> stringify(2, False, None)
    ['2', 'False', 'None']

    The following code shows you a legitimate use of **kwargs in function declaration. Similarly, the function with **kwargs allows the users to set any number of keyword arguments, to make your function more flexible.

    以下代码显示了在函数声明中合法使用**kwargs情况。 同样,带有**kwargs的函数允许用户设置任意数量的关键字参数,以使您的函数更灵活。

    >>> # Define a function that accepts undetermined keyword arguments
    ... def generate_score_reports(name, **kwargs):
    ...     print(f"***** Report for {name} *****")
    ...     for key, value in kwargs.items():
    ...         print(f"### {key}: {value}")
    ...     print("***** Report End *****\n")
    ... 
    ... scores = {"John": {"math": 99, "phys": 97},
    ...           "Jan": {"math": 94, "bio": 98}}
    ... 
    >>> for name, scores in scores.items():
    ...     generate_score_reports(name, **scores)
    ... 
    ***** Report for John *****
    ### math: 99
    ### phys: 97
    ***** Report End *****
    
    
    ***** Report for Jan *****
    ### math: 94
    ### bio: 98
    ***** Report End *****

    However, in most cases, you don’t need to use *args or **kwargs. Although it can make your declaration a bit cleaner, it hides the function’s signature. In other words, the users of your functions have to figure out exactly what parameters your functions take. So my advice is to avoid using them if you don’t have to. For instance, can I use a dictionary argument to replace the **kwargs? Similarly, can I use a list or tuple object to replace *args? In most cases, these alternatives should work without any problems.

    但是,在大多数情况下,您不需要使用*args**kwargs 。 尽管它可以使声明更清晰,但它会隐藏函数的签名。 换句话说,函数的用户必须准确地确定函数采用的参数。 因此,我的建议是避免在不需要时使用它们。 例如,我可以使用字典参数来替换**kwargs吗? 同样,我可以使用列表或元组对象替换*args吗? 在大多数情况下,这些替代方法应该可以正常工作。

    9.参数的类型注释 (9. Type Annotation for Arguments)

    As mentioned previously, Python is a dynamically-typed programming language as well as an interpreted language, the implication of which is that Python doesn’t check code validity, including type compatibility, during coding time. Until your code actually executes, will type incompatibility with your function (e.g., send a string to a function when an integer is expected) emerge.

    如前所述,Python是一种动态类型的编程语言,也是一种解释型语言,其含义是Python在编码期间不会检查代码的有效性,包括类型兼容性。 在您的代码实际执行之前,将出现与您的函数不兼容的类型(例如,在期望整数时将字符串发送给函数)。

    For these reasons, Python doesn’t enforce the declaration of the type of input and output arguments. In other words, when you create your functions, you don’t need to specify what types of parameters they should have. However, it has become possible to do that in recent Python releases. The major benefit of having type annotation is that some IDEs (e.g., PyCharm or Visual Studio Code) could use the annotations to check the type compatibility for you, so that when you or other users use your functions you can get proper hints.

    由于这些原因,Python不会强制执行输入和输出参数类型的声明。 换句话说,在创建函数时,无需指定它们应具有的参数类型。 但是,在最近的Python版本中可以做到这一点。 具有类型注释的主要好处是某些IDE(例如PyCharm或Visual Studio Code)可以使用注释来检查类型的兼容性,以便当您或其他用户使用您的函数时,您可以获得正确的提示。

    Another related benefit is that if the IDEs know the type of parameter, it can give proper auto-completion suggestions to help you code faster. Certainly, when you write docstrings for your functions, these type annotations will also be informative to the end developers of your code.

    另一个相关的好处是,如果IDE知道参数的类型,它可以给出适当的自动完成建议,以帮助您更快地进行编码。 当然,当您为函数编写文档字符串时,这些类型注释也将为代码的最终开发人员提供信息。

    Image for post
    Helpful Information With Type Annotation (PyCharm)
    带有类型注释的有用信息(PyCharm)

    10.负责文件 (10. Responsible Documentation)

    I equate good documentation with responsible documentation. If your functions are for private uses, you don’t have to write very thorough documentation — you can make the assumption that your code tells the story clearly. If anywhere requires some clarification, you can write a very brief comment that can serve as a reminder for yourself or other readers when your code is revisited. Here, the discussion of responsible documentation is more concerned with the docstrings of your function as public APIs. The following aspects should be included:

    我把好的文档与负责的文档等同起来。 如果您的函数是供私人使用的,则不必编写非常详尽的文档-您可以假设您的代码清楚地讲述了故事。 如果任何地方都需要澄清,您可以写一个简短的注释,当您重新访问代码时,它可以提醒您自己或其他读者。 在这里,有关负责任文档的讨论更多地与作为公共API的函数的文档字符串有关。 应包括以下方面:

    • A brief summary of the intended operation of your function. This should be very concise. In most cases, the summary shouldn’t be more than one sentence.

      功能预期操作的简短摘要。 这应该非常简洁。 在大多数情况下,摘要不应超过一个句子。

    • Input arguments: Type and explanation. You need to specify what type of your input arguments should be and what they can do by setting particular options.

      输入参数:类型和说明。 您需要指定输入参数的类型,以及通过设置特定选项可以执行的操作。

    • Return Value: Type and explanation. Just as with input arguments, you need to specify the output of your function. If it doesn’t return anything, you can optionally specify None as the return value.

      返回值:类型和说明。 与输入参数一样,您需要指定函数的输出。 如果不返回任何内容,则可以选择将None指定为返回值。

    结论 (Conclusions)

    If you’re experienced with coding, you’ll find out that most of your time is spent on writing and refactoring functions. After all, your data usually doesn’t change too much itself— it’s the functions that process and manipulate your data. If you think of data as the trunk of your body, functions are the arms and legs that move you around. So, we have to write good functions to make our programs agile.

    如果您有编码经验,您会发现大部分时间都花在编写和重构函数上。 毕竟,您的数据本身通常不会发生太大变化,而是处理和操纵数据的功能。 如果您将数据视为身体的躯干,那么功能就是手臂和腿部。 因此,我们必须编写好的函数来使我们的程序敏捷。

    I hope that this article has conveyed some useful information that you can use in your coding.

    我希望本文传达了一些可以在编码中使用的有用信息。

    Thanks for reading.

    谢谢阅读。

    翻译自: https://medium.com/better-programming/advanced-python-consider-these-10-elements-when-you-define-python-functions-61c0be8a10ed

    python函数内定义函数

    展开全文
  • 同学们学好函数高中数学的枢纽章节函数,对于同学学习其他的章节一定会有很好的帮助,今天老师给大家将函数的三要素定义域。 定义域是函数的灵魂,我在做题的时候忘掉什么,都不能忘掉定义域;如果忘掉定义域...

    高中数学函数
    作者:vxbomath
    同学们学好函数这个高中数学的枢纽章节函数,对于同学学习其他的章节一定会有很好的帮助,今天老师给大家将函数的三要素的定义域。
    定义域是函数的灵魂,我在做题的时候忘掉什么,都不能忘掉定义域;如果忘掉定义域做题的时候就会出现错误的,不管是求函数问题,还是解方程过程一定要记住。定义域是函数的灵魂;不能忘掉。
    再解已知解析式型的时候定义域用四个类型能想全面:
    函数定义域

    定义域是函数的灵魂这个点大家不要忘,因为这个点并不是考察同学们的学习能力,而是考察同学们细不细心;如果同学们在做函数问题的时候没有条件反射想定域,那么这个点很有可能会出错,这点错后面的点就会跟着错。我们通过一个例题把四个类型都讲一遍;接着看题:函数

    审题:根号下x,x大于等于0,出现了根号,就要强调x减1大于等于0,还要强调x分之1分母不为0,所以x减一不等于0;在强调x的0次幂,x不等于0,这里出现了(x-3)的0次幂,所以x减3不对等于0;最后强调log这里的X被称为真数,所以x的2次幂减4是大于0,这是四个点我们要结合来接题;看1和2是X大于1的,第三个是x不等三的,第四个是x平方大于4写成x大于2或者x小于负二,在去他的交集,我们在取交集的时候由于x不等于3.所以我们不要看它。同学们我要的是每个区间上出现连个线段的部分。这就说明:x属于2到3并3到正无穷大;

    接下来我讲抽象函数:
    解抽象函数记住这两句话:
    抽象函数

    这里我们要注意这里的X的本身,这里的本身二字非常重要,第二句里的f()内部的限定是一致的,是被称为解这类题的桥梁;
    看题:
    函数

    审题:已知f(x)的定义域是3到5,说定域就是说X的本身范围。那么求f(2x-1)定义域就是求x本身范围。f(x)的定义域为3到5.我就认为x小于等于5、大于等于3,现在让我们求f(2x-1)的定义域是在求X范围这里我们就要使用桥梁,因为放入f()里的限制是一样的。所以认为2-x也是小于等于5、大于等于3,说明x小于等于3、大于等于;所f(2x-1)的定义域为2到3;函数定义域

    接着看下一题:函数

    审题:已知f(2x-1)的定义域为3到5,求f(x)的定义域;说定义域就是说x的本身。所以x小于等于5、大于等于3;求f(x)的定义域 ,找到f()内部的限制 ,根据已知发现是2x-1放进去了;所以根据X小于等于5、大于等于3;得出2x-1的范围是小于等于9、大于等于5;用桥梁把f(x)的限定放进去,就得到x小于等于9、大于等于5;所以f(x)的定义域为5到9;
    函数
    接着看下一题:
    函数

    审题:已知f(2x-1)的定义域是3到5,求f(4x-1)的定义域.说定义域就是说x本身;说明x大于等于3、小于等于5,所以2x-1大于等于5、小于等于9;桥梁是4x减1大于等于5、小于等于9;求定义域就是求X本身范围,所以反解x;x小于等于2分之5、大于等于2分之3,;所以f(4x-1)的定义域为2分之3到2分之5;
    抽象函数

    接下来讲的是已知定义域求参数范围;
    函数
    审题;发现这道题含参数K了,它说定义域为R,求k的取值范围,不管x取什么值的时候,根号下面的式子是恒大得0的,遇到这样的题型不管函数、不等式、方程问题只要最高思想含参了;养成好习惯谈论参数是否存在;
    假设两种情况:
    1、k得0,f(x)等于根号1,已知定义域是R,那么x就属于R,x在取什么值得时候都是根号1,所以根号1是成立的;
    2、k不得0,就是2次式了,这里就认为是二次函数式,会发现有无穷个点使得函数值是小0的。那么这样的函数值放在根号里面是没有意义的;
    所以必须要k大于0 ,会发现k大于0也是不成立的;
    Delta小于等于0,等于0也是可以,因为根号0,也是有意义;现在来解题k大于0,Delta=k方减4k小于等于0;在解k大于0,k大于等于0、小于4;在取交集所以k大于0、小于等于4;在根据第1第2个点去并集所以k属于0到4;
    函数

    今天的知识就分享到这里,需要更多高质量的解题技巧可以私聊老师,可以评论在下方老师会统一回复大家!

    展开全文
  • C++函数定义与使用

    千次阅读 多人点赞 2018-07-13 14:33:05
    C++函数的定义与使用main就是一函数,它是C++程序的主函数。一C++程序可以由一主函数和若干子函数组成。...1函数的定义1.1函数定义的语法形式类型说明符 函数名(含类型说明的形式参数表){ 语句序...

    C++函数的定义与使用

    main就是一个函数,它是C++程序的主函数。一个C++程序可以由一个主函数和若干子函数组成。主函数是程序执行的开始点。由主函数调用子函数,子函数还可以再调用其它子函数

    调用其它函数的函数称为主调函数。被其他函数调用的函数称为被调函数。一个函数很可能既调用别的函数又被其它函数调用。

    1函数的定义

    1.1函数定义的语法形式

    类型说明符   函数名(含类型说明的形式参数表)

    {

      语句序列

    }

    1.2形式参数

    类型标识符1   形参名1,类型标识符2   形参名2,···,类型标识符n   形参名n

    形参的作用是实现主调函数与被调函数之间的联系。通常将函数所处理的数据、影响函数功能的因素或者函数的处理结果作为形参。

    如果一个函数的形参表为空,则表示它没有任何形参。main函数可以没有形参,也可以有形参,其形参也称命令行参数,由操作系统在启动程序时初始化。

    函数在没有被调用时是静止的,此时的形参只是一个符号,它标志着在形参出现的位置应该有一个什么类型的数据。

    函数在被调用时才执行,也就是在被调用时才由主调函数将实际参数赋予形参。

    1.3函数的返回值和返回值类型

    函数可以有一个返回值,函数的返回值是需要返回给主调函数的处理结果。类型说明符规定了函数返回值的类型,函数的返回值由return语句给出,格式如下:

    return 表达式;

    除了指定函数的返回值外,return语句还有一个作用,就是结束当前函数的执行

    一个函数也可以不讲任何值返回给主调函数,这时它的类型标识符为void,可以不写return语句,但也可以写一个不带表达式的return语句,用于结束当前函数的调用,格式如下:

    return;

    2.函数的调用

    2.1函数的调用形式

    变量在使用之前需要首先声明,类似的,函数在调用之前也需要声明。函数的定义就属于函数的声明,因此,在定义了一个函数之后,可以直接调用这个函数。但如果希望在定义一个函数之前调用它,则需要在调用函数之前添加该函数的函数原型声明。函数原型声明的形式如下:

    类型说明符   函数名(含类型说明的形参表);

    与变量的声明和定义类似,声明一个函数只是将函数的有关信息告诉编译器,此时并不产生任何代码;定义一个函数是除了同样要给出函数的有关信息外,主要是要写出函数的代码。

    声明了函数原型之后,便可以按如下形式调用子函数:

    函数名(实参列表)

    实参列表应该给出与函数原型形参个数相同、类型相符的实参,每个实参都是一个表达式。函数调用可以作为一条语句,这时函数可以没有返回值。函数调用也可以出现在表达式中,这时就必须有一个明确的返回值。

     

    例1  编写一个求x的n次方的函数

    #include<iostream>
    using namespace std;
    double power(double x,int n)
    {
    double val=1.0;
    while(n--)
    val*=x;
    return val;
    }
    int main()
    {
    cout<<"5 to the power 2 is"<<power(5,2)<<endl;
    return 0;

    } 



    例2  输入一个8位二进制数,将其转换为十进制数输出

    #include<iostream>
    using namespace std;
    double power(double x,int n);
    int main(){
    int value=0;
    cout<<"Enter an 8 bit binary number: ";
    for(int i=7;i>=0;i--)
    {
    char ch;
    cin>>ch;
    if(ch=='1')
    value+=static_cast<int>(power(2,i));
    }
    cout<<"Decimal value is"<<value<<endl;
    return 0;
    }
    double power(double x,int n)
    {
    double val=1.0;
    while(n--)
    val*=x;
    return val;

    }

    注意:ch的类型写成int是不正确的



    例3   编写程序求π的值,公式如下:

    π=16arctan(1/5)-4arctan(1/239)

    其中arctan用如下形式的级数计算:

       arctanx=x-x^3/3+x^5/5-x^7/7+···

    直到级数某项绝对值不大于10^-15为止;π和x均为double型。

    #include<iostream>
    using namespace std;
    double arctan(double x)
    {
    double sqr=x*x;
    double e=x;
    double r=0;
    int i=1;
    while(e/i>1e-15)//1e-15
    是科学记数法的1*10-15次方
    {
    double f=e/i;
    r=(i%4==1)?r+f:r-f;
    e=e*sqr;
    i+=2;
    }
    return r;
    }
    int main()
    {
    double a=16.0*arctan(1/5.0);
    double b=4.0*arctan(1/239.0);
    cout<<"PI="<<a-b<<endl;
    return 0;
    }




    例4  寻找并输出11~999之间的数m,它满足m,m^2,m^3均为回文数

    所谓回文数是指其各位数字左右对称的整数。例如,121,676,94249等。满足上述条件的数如m=11,m^2=121,m^3=1331.

    分析:判断一个数是否是回文数,可以用除以10求余的方法,从最低位开始,依次取出该数的各位数字,然后用最低位充当最高位,按反序重新构成新的数,与原数比较是否相等,若相等,则原数为回文。

    #include<iostream>
    using namespace std;
    //判断n是否为回文数
    bool symm(unsigned n)
    {
    unsigned i=n;
    unsigned m=0;
    while(i>0)
    {
    m=m*10+i%10;
    i/=10;
    }
    return m==n;
    }

    int main()
    {
    for(unsigned m=11;m<=999;m++)
    {
    if(symm(m)&&symm(m*m)&&symm(m*m*m))
    {
    cout<<"m="<<m;
    cout<<"   m*m="<<m*m;
    cout<<"   m*m*m="<<m*m*m<<endl;
    }
    }
    return 0;
    }


     

    例5   投骰子的随机游戏

     游戏规则是:每个骰子有六面,点数分别为1,2,3,4,5,6。游戏者在程序开始时输入一个无符号整数,作为产生随机数的种子。每轮投两次骰子,第一轮如果和数为7或者11则为胜,游戏结束;和数为2,3或者12则为负,游戏结束;和数为其他值则将此值作为自己的点数,就像第二轮、第三轮···直到某轮的和数等于点数则取胜,若在此前出现和数为7则为负。

    有rollDice函数负责模拟投骰子,计算和数并输出和数。

    提示  系统函数int rand(void)的功能是产生一个伪随机数,伪随机数不是真正随机的。这个函数自己不能产生真正的随机数。如果在程序中连续调用rand,期望由此可以产生一个随机数序列,你会发现每次运行这个程序时产生的序列都是相同的,这称为伪随机数序列。这是因为函数rand需要一个称为”种子“的初始值,种子不同,产生的伪随机数也就不同。因此只要每次运行时给予不同的种子,然后连续调用rand便可以产生不同的随机数序列。如果不设置种子,rand总是默认种子为1。不过设置种子的方法比较特殊,不是通过函数的参数,而是在调用它之前,需要首先调用另外一个函数voidsrandunsignedint seed为其设置种子,其中的参数seed便是种子。

    #include<iostream>
    #include<cstdlib>
    using namespace std;


    int rollDice(){
    int die1=1+rand()%6;
    int die2=1+rand()%6;
    int sum=die1+die2;
    cout<<"Playerrolled"<<die1<<"+"<<die2<<"="<<sum<<endl;
    return sum;
    }
    enum GameStatus{WIN,LOSE,PLAYING};
    int main()
    {
    int sum,myPoint;
        GameStatus status;


    unsigned seed;
    cout<<"Please enter an unsigned integer:";
    cin>>seed;
    srand(seed);


    sum=rollDice();
    switch(sum)
    {
    case 7:
    case 11:
    status=WIN;
    break;
    case 2:
    case 3:
    case 12:
    status=LOSE;
    break;
    default:
    status=PLAYING;
    myPoint=sum;
    cout<<"point is"<<myPoint<<endl;
    break;
    }
    while(status==PLAYING)
    {
    sum=rollDice();
    if(sum==myPoint)
    status=WIN;
    else if(sum==7)
    status=LOSE;


    }
    if(status==WIN)
    cout<<"player wins"<<endl;
    else
    cout<<"player loses"<<endl;
    return 0;
    }


    2.2嵌套调用

    函数允许嵌套调用。如果函数1调用了函数2,函数2再调用函数3,便形成了函数的嵌套调用

    例1   输入两个整数,求他们的平方和。

    分析:设计两个函数:求平方和函数fun1和求一个整数的平方函数fun2。由主函数调用fun1,fun1又调用fun2。

    #include<iostream>
    using namespace std;
    int fun2(int m)
    {
    return m*m;
    }
    int fun1(int x,int y)
    {
    return fun2(x)+fun2(y);
    }
    int main()
    {
    int a,b;
    cout<<"Please enter two integers(a and b):";
    cin>>a>>b;
    cout<<"The sum of square of a andb:"<<fun1(a,b)<<endl;
    return 0;

    }

    2.3递归调用

    函数可以直接或间接地调用自身,称为递归调用

    所谓直接调用自身,就是指在一个函数的函数体中出现了对自身的调用表达式,例如:

    voidfun1()

    {

    ···

    fun1();    //调用fun1()自身

    ···

      

    }

    这就是函数直接调用自身的例子。

    而下面的情况是函数间接调用自身:

    voidfun1()

    {

    ···

    fun2();

    ···

    }

    voidfun2()

    {

    ···

    fun1();

    ···

    }

    这里fun1调用了fun2,而fun2又调用了fun1,于是构成了递归。

    递归算法的实质是将原有的问题分解成新的问题,而解决新问题时又用到了原有问题的解法。按照这一原则分解下去,每次出现的新问题都是原有问题的简化的子集,而最终分解出来的问题,是一个已知解的问题。这便是有限的递归调用。只有有限的递归调用才是有意义的,无限的递归调用永远得不到解,没有实际意义。

    递归的过程有如下两个阶段。

    第一阶段:递推。将原问题不断分解为新的子问题,逐渐从未知向已知推进,最终达到已知的条件,即递归结束的条件,这时递推阶段结束。

    例如,求5!,可以这样分解:

    5!=5*4!—>4!=4*3!—>3!=3*2!—>2!=2*1!—>1!=1*0!—>0!=1

    未知----------------------------------------------------------->已知

    第二阶段:回归。从已知的条件出发,按照递推的逆过程,逐一求值回归,最后达到递推的开始处,结束回归阶段,完成递归调用。

    例如,求5!的回归阶段如下:

    5!=5*4!<—4!=4*3!<—3!=3*2!<—2!=2*1!<—1!=1*0!<—0!=1

    未知<-----------------------------------------------------------已知

    例1   求n!

    分析:当n=0时,n!=1;

              当n>0时,n=n*(n-1)!

    递归结束的条件是n=0。

    #include<iostream>
    using namespace std;
    unsigned fac(unsigned n)
    {
    unsigned f;
    if(n==0)
    f=1;
    else
    f=fac(n-1)*n;
    return f;
    }
    int main()
    {
    unsigned n;
    cout<<"Enter a positive integer:";
    cin>>n;
    unsigned y=fac(n);
    cout<<n<<"!="<<y<<endl;
    return 0;

    }

    注意:对同一个函数的多次不同调用中,编译器会为函数的形参和局部变量分配不同的空间,他们互不影响。

    例2   用递归法计算从n个人中选择k个人组成一个委员会的不同组合数。

    分析:由n个人里选k个人的组合数=由n-1个人里选k个人的组合数+由n-1个人里选k-1个人的组合数

    由于计算公式本身是递归的,因此可以编写一个递归函数来完成这一功能,递推的结束条件是n=k或n=0,这时的组合数是1,然后开始回归。

    #include<iostream>
    using namespace std;
    int comm(int n,int k)
    {
    if(n<k)
    return 0;
    else if(n==k||k==0)
    return 1;
    else
    return comm(n-1,k)+comm(n-1,k-1);
    }
    int main()
    {
    int n,k;
    cout<<"Please enter two integers n and k:";
    cin>>n>>k;
    cout<<"C(n,k)="<<comm(n,k)<<endl;
    return 0;

    }

    例3   汉诺塔问题

    有三根针A,B,C。A针上有n个盘子,盘子大小不等,大的在下,小的在上。要求把这n个盘子从A针移动到C针,在移动的过程中可以借助B针,每次只允许移动一个盘子,且在移动过程中在三针上都保持大盘在下,小盘在上。

    分析:将n个盘子从A针移动到C针可以分解为下面三个步骤:

    (1)将A上n-1个盘子移动到B针(借助C针);

    (2)将A针上剩下的一个盘子移到C针上;

    (3)将n-1个盘子从B针移到C针(借助A针)。

    事实上,上面三个步骤包含下面两种操作:

    (1)将多个盘子从一个针移动到另一个针上,这是一个递归的过程。

    (2)将一个盘子从一个针移到另一个针上。

    #include<iostream>
    using namespace std;
    void move(char src,char dest)
    {
    cout<<src<<"-->"<<dest<<endl;
    }
    void hanoi(int n,char src,char medium,char dest)
    {
    if(n==1)
    move(src,dest);
    else
    {
    hanoi(n-1,src,dest,medium);
    move(src,dest);
    hanoi(n-1,medium,src,dest);
    }
    }
    int main()
    {
    int m;
    cout<<"Enter the number of disks:";
    cin>>m;
    cout<<"the steps tomoving"<<m<<"disks:"<<endl;
    hanoi(m,'A','B','C');
    return 0;

    }

    3.函数的参数传递

    在函数未被调用时,函数的形参并不占有实际的内存空间也没有实际的值只有在函数被调用时才为形参分配存储单元,并将实参与形参结合。每个实参都是一个表达式,其类型必须与形参相符。函数的参数传递指的就是形参与实参结合(简称形实结合)的过程,形实结合的方式有值传递和引用传递。

    3.1值传递

    值传递是指当发生函数调用时,给形参分配内存空间,并用实参来初始化形参(直接将实参的值传递给形参)。这一过程是参数值的单项传递过程,一旦形参获得了值便与实参脱离关系,此后无论形参发生了怎样的改变,都不会影响到实参。

    例1    将两个整数变换次序后输出

    #include<iostream>
    using namespace std;
    void swap(int a,int b)
    {
    int t=a;
    a=b;
    b=t;
    }
    int main()
    {
    int x=5,y=10;
    cout<<"x="<<x<<"  y="<<y<<endl;
    swap(x,y);
    cout<<"x="<<x<<"  y="<<y<<endl;
    return 0;

    }

    分析:从上面的运行结果可以看出,并没有达到交换的目的。这是因为,采用的是值传递,函数调用时传递的是实参的值,是单向传递过程。形参值的改变对实参不起作用。

    3.2 引用传递

    值传递时参数是单向传递。

    引用是一种特殊类型的变量,可以被认为是另一个变量的别名,通过引用名与通过被引用的变量名访问变量的效果是一样的。例如:

    inti,j;

    int&ri=i;  //建立一个int型的引用ri,并将其初始化为变量i的一个别名

    j=10;

    ri=j;        //相当于i=j

    使用引用时必须注意以下问题:

    ·声明一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象。44

    ·一旦一个引用被初始化后,就不能改为指向其它对象。

    也就是说,一个引用,从它诞生时,就必须确定是哪个变量的别名,而且始终只能作为作为这一个变量的别名,不能另作他用。

    引用也可以作为形参,如果将引用作为形参,情况便稍有不同。这是因为,形参的初始化不在类型说明时进行,而是在执行主调函数中的调用表达式时,才为形参分配内存空间,同时用实参初始化形参。这样引用类型的形参就通过形实结合,成为了实参的一个别名,对形参的任何操作也就会直接作用于实参。

    用引用作为形参,在函数调用时发生的参数传递,称为引用传递。

    例2    使用引用传递改写例1,使两整数成功的进行交换

    #include<iostream>
    using namespace std;
    void swap(int &a,int &b)
    {
    int t=a;
    a=b;
    b=t;
    }
    int main()
    {
    int x=5,y=10;
    cout<<"x="<<x<<"  y="<<y<<endl;
    swap(x,y);
    cout<<"x="<<x<<"  y="<<y<<endl;
    return 0;

    }

    例3    值传递与引用传递的比较

    #include<iostream>
    #include<iomanip>
    using namespace std;


    void fiddle(int in1,int &in2)
    {
    in1=in1+100;
    in2=in2+100;
    cout<<"The values are";
    cout<<setw(5)<<in1;
    cout<<setw(5)<<in2<<endl;
    }
    int main()
    {
    int v1=7,v2=12;
    cout<<"The values are";
    cout<<setw(5)<<v1;
    cout<<setw(5)<<v2<<endl;
    fiddle(v1,v2);
    cout<<"The values are";
    cout<<setw(5)<<v1;
    cout<<setw(5)<<v2<<endl;
    return 0;
    }


     

    展开全文
  • 函数的传统定义:设在某变化过程中有两个变量x、y,如果对于x在某一范围内的每一个确定的值,y都有唯一确定的值与它对应,那么就称y是x的函数,x叫做自变量。我们将自变量x取值的集合叫做函数定义域,和自变量x...
  • 有关于这个功能的两个硬盘部分这是一种混乱。1. lambda a, b: b*a(a, b-1) if b > 0 else 1。2. “B” 这是如下因素1.为1,这不外乎:def f(a, b):if b > 0:b * a(a, b - 1)else:1对于2,本B(lambda b: (lambda a, b...
  • 函数是Javascript的最重用的内容,首先,要知道函数也是一对象,要使用函数,一般要先定义,那么定义的方法有几种呢?可以分为种:语句定义和表达式定义。使用最普遍的使用function语句。 例如1.1:  ...
  • C++函数声明和定义

    千次阅读 2016-04-12 11:06:44
    在上一节,我们已经学会了如何阅读函数原型和如何调用一个函数。然而,仅靠系统给出的标准库函数是不够用的。我们有时候要根据我们的实际要求...其次,我们要告诉电脑这个函数是怎么运作的,这叫作函数定义(Definiti
  • 父类与子类,函数不是虚函数(分一在父类一在子类、同在父类或同在子类种情况),只有重载;函数为虚函数,当一在父类一在子类,只有重写(覆盖);同在父类或同在子类,只有重载。 以下为转载内容: 写这篇...
  • 高等数学 函数定义

    千次阅读 2018-04-29 16:01:02
    设 ε 为任一给定的正数,则集合{x x - a &lt; ε} 称为点 a 的 ε 邻域,它表示以 a 点为中心,...ε (ε)艾普西隆球体体积v=4πR³/3定义 1 设 x、 y 是同一过程中的两个变量, 若当 x 在数集 D 内取任一值...
  • 函数定义中,函数返回类型前加上关键字inline即把min()指定为内联。 inline int min(int first, int secend) {/****/}; inline 函数对编译器而言必须是可见的,以便它能够在调用点内展开该函数。与非...
  • 语言宏定义与预处理、函数函数库 目录 1、C语言预处理理论 2、C语言预处理代码实战 3、宏定义1 4、宏定义2 5、.函数的本质 6、.函数的基本使用 7、递归函数 8、函数库 9、字符串函数 10、数学库函数 11、自己制作...
  • java中函数定义和使用

    万次阅读 2018-07-22 22:28:10
    什么是函数函数是一段可以完成某个独立...函数设计三要素: (1)函数名:见名思议,也就是根据功能定名字,按照驼峰写法xxxYyyZzz (2)参数:调用者传递过来的数据的接收方式,参数可以有多,也可以没有...
  • 一元函数,多元函数,可微的含义:就是用极限的思想近似反应两个可变因素之间的函数关系。近似代替。 一元函数微分的几何意义:就是曲线x增加了一部分,y增加多少的表示。用了极限分割的思想,无线接近。dy与△y...
  • inline函数的声明与定义

    千次阅读 2015-04-04 10:26:32
    函数声明或定义函数返回类型前加上关键字inline即把min()指定为内联。  inline int min(int first, int secend) {/****/};  inline 函数对编译器而言必须是可见的,以便它能够在调用点内展开该函数。与非...
  • 内联函数的声明和定义

    千次阅读 2015-05-12 09:51:32
    内联函数的声明和定义 一、什么叫inline函数?  inline(小心,不是online),翻译成“内联”或“内嵌”。意指:当编译器发现某段代码在调用一内联函数时,它不是去调用该函数,而是将该函数的代码,整段...
  • 借用该函数验证哥德巴赫猜想:任意一个大的偶数都可以分解成两个素数之和。从键盘输入一个偶数,输出该偶数的两个素数之和。 代码段: #include <iostream> #include <stdio.h> #include <cmath> ...
  • 函数

    千次阅读 2021-02-17 18:49:33
    复合函数:如果内层函数的值域与外层函数定义域的交集非空,这两个函数才能复合,否则不能复合。 反函数函数y=f(x)y=f(x)y=f(x)的定义域为D,值域为RyR_yRy​。若对任意y∈Ryy \in R_yy∈Ry​,有唯一确定的x∈...
  • 什么是递归函数

    万次阅读 多人点赞 2018-02-21 09:42:10
    递归函数 递归 例题 特点 效率 优点 ...递归函数 ...递归就是一个函数在它的函数...所以递归要有两个要素,结束条件与递推关系 注: 递归的时候,每次调用一个函数,计算机都会为这个函数分配新的空间,这就是说,当...
  • 交叉熵损失函数原理详解

    万次阅读 多人点赞 2019-08-30 10:28:42
    之前在代码中经常看见交叉熵损失函数(CrossEntropy Loss),只知道它是分类问题中经常使用的一种损失函数,对于其内部的原理总是模模糊糊,而且一般使用交叉熵作为损失函数时,在模型的输出层总会接一softmax函数,...
  • δ}= N(a,δ)\{a}二:函数的概念函数定义:设有两个数集 X,Y,f是一个确定的对应规律,x属于X,通过对应法则f 都有唯一一个y属于Y,与x对应,记为x-&gt;y ,或f(x) = y, 则称对应法则f为定义在X上的函数其中X...
  • 这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是1M(也有的说是2M,总之是一编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的...
  • 《高斯核函数点性质》

    万次阅读 2014-03-15 10:18:23
    高斯核函数点性质  高斯核函数 K(x,y)=exp(-||x-y||2/2σ2) 在选择核函数时,若对给出的数据没有先验知识,RBF核就是最好的选择。为了研究为什么使用了核技巧的学习机器往往具有良好的推广能力,文献[1]...
  • 函数定义:函数的声明和函数的实现 定义语法: public static void main(String[] args){ //函数主体 } 定义位置:函数定义在类的内部,与main函数并列 函数的调用:函数名(); 函数的参数 调用函数是所传入...
  • ====================================================|| 欢迎讨论技术的可以相互加微信:windgs (请备注csdn+xx职业) =================================================...(函数要素:返回类型,函数名,参...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 172,843
精华内容 69,137
关键字:

函数定义两个要素