【函数】一篇文章带你看懂控制流、递归、高阶函数

news2024/11/15 9:50:00

目录

控制流

条件语句

迭代语句

示例:质因数分解

 

递归

示例:阶乘

示例:斐波那契数列

示例:判断奇偶数

高阶函数

lambda 表达式

设计函数

示例:累加计算

示例:柯里化

Lab 1: Functions, Control

Homework 2: Higher Order Functions

Project 1: The Game of Hog


控制流

解释器所执行语句来执行某些操作。

比如这整个复合语句 (compound statement),在 Python 中由 def 声明;标头 header 确定了一个简易语句 (clause) 的类型,这个语句中跟随了一个语句序列 (suite)。解释器会按一定顺序执行这个语句序列。

条件语句

条件语句在大部分语言中以 if 关键字呈现。

在 Python 中 True 和 False 分别表示真或假,if 引导条件语句及其真分支,零或一个 else 引导假分支,其中还可能会有零或多个 elif 进行嵌套。

def absolute_value(n):
    if n < 0:
        return -n
    elif n == 0:
        return 0
    else:
        return n

 在 scheme 中 #t 和 #f 分别表示真或假,语法的话就不能 elif 进行嵌套了 (if test consequent alternative)

(define (absolute-value n)
  (if (positive? n) n (- n)))

FP 中一般都会提供一套类似 guard 的语法,即该条件语句可以接受任意多的条件判断,由上至下进行条件判断,在条件为真时执行语句块并退出条件语句,如果所有条件都不符合将有一个默认块进行兜底处理。其实这个语句更像是 if-then-elif-else 的变体。

(cond ((< n 0) (- n))
      ((= n 0) n)
      (else n))

在 erlang 中,if 就是 cond。

absolute_value(N) ->
    if
        N < 0 -> -N;
        N =:= 0 -> N;
        true -> N
    end.

迭代语句

迭代语句也称循环语句,在满足条件的情况下运行循环体,直到不满足的情况下退出。

i = 0
total = 0
while i < 3:
    total = total + i
    i = i + 1
    print(total)

print(total)
# 0
# 1
# 3

scheme 中的循环与正常语言中的 while 差距有点大,虽然更像是 C 语言中的 for (也可能是 C 的 for 更像是 lisp 的 do),无论怎么说,都是一种带变量变化的循环语句。

(do ((i 0 (+ i 1)) (total 0 (+ total i))) ;; assignment
    ((> i 2) total) ;; exit
  (display total) ;; body
  (newline))
;; 0
;; 0
;; 1
;; 3 -- not print

 erlang 作为一种 pure 的 FP 并没有可用的 while 语句,需要使用尾递归来模拟。

loop(3, Total) -> Total;
loop(I, Total) -> loop(I + 1, Total + I).

 

示例:质因数分解

质因数分解是一个典型的需要用循环、条件来查找出答案的问题。对于每个正整数 N,我们都可以分解出它的所有质因数集合:

  • 8 = 2 * 2 * 2
  • 9 = 3 * 3
  • 10 = 2 * 5
  • 11 = 11

比较好的一种方式是,寻找这个正整数的最小质因数,之后再继续分解剩余的部分,直到分解完成。

858 = 2∗429 = 2∗3∗143 = 2∗3∗11∗13

(define (get-primes n)
  (let ((bits (make-vector (+ n 1) #t)))
    (let loop ((p 2) (ps '()))
      (cond ((< n p) (reverse ps))
            ((vector-ref bits p)
             (do ((i (+ p p) (+ i p))) ((< n i))
               (vector-set! bits i #f))
             (loop (+ p 1) (cons p ps)))
            (else (loop (+ p 1) ps))))))

(define (get-factorization n primes)
  (let ((prime (car primes))
        (others (cdr primes)))
    (if (= (remainder n prime) 0)
        prime
        (get-factorization n others))))

(define (prime-factorization n)
  (if (< n 3)
      (list n)
      (let ((primes (get-primes n)))
        (let loop ((num n) (ans '()))
          (cond ((= num 1) (reverse ans))
                (else (let ((prime (get-factorization num primes)))
                        (loop (quotient num prime) (cons prime ans)))))))))

 

递归

递归 (Recursion) 在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法重复事物的过程。例如,当两面镜子相互之间近似平行时,镜中嵌套的图像是以无限递归的形式出现的。也可以理解为自我复制的过程。

以下是一个可能更有利于理解递归过程的解释:

  1. 我们已经完成了吗?如果完成了,返回结果。如果没有这样的终止条件,递归将会永远地继续下去。
  2. 如果没有,则简化问题,解决较容易的问题,并将结果组装成原始问题的解决办法。然后返回该解决办法。

示例:阶乘

Factorial(0)=1,

Factorial(1)=1,

Factorial(N)=N×Factorial(N−1)

(define (factorial n)
  (cond ((= n 0) 1)
        (else (* n (factorial (- n 1))))))

 这个计算过程中,通过代换模型可以看出计算是一种先逐步展开而后收缩的形状,计算过程构造起一个推迟进行的操作所形成的链条,收缩阶段表现为这些运算的实际执行,这种计算过程被称为递归计算过程。如果要执行这个过程,解释器就必须维护好以后要执行的操作的轨迹,这个例子中推迟执行的乘法链条的长度也就是为保存其轨迹需要保存的信息量,这个长度随着 n 值的增加而线性增长,这个过程被称为线性递归计算

(factorial 5)
(* 5 (factorial 4))
(* 5 (* 4 (factorial 3)))
(* 5 (* 4 (* 3 (factorial 2))))
(* 5 (* 4 (* 3 (* 2 (factorial 1)))))
(* 5 (* 4 (* 3 (* 2 (* 1 (factorial 0))))))
(* 5 (* 4 (* 3 (* 2 (* 1 1)))))
(* 5 (* 4 (* 3 (* 2 1))))
(* 5 (* 4 (* 3 2)))
(* 5 (* 4 6))
(* 5 24)
120

 来看另一种实现

(define (factorial n)
  (let factorial-iter ((product 1) (counter 1))
    (if (> counter n)
        product
        (factorial-iter (* counter product)
                        (1+ counter)))))

 这个计算过程中没有任何增长或收缩,计算过程的每一步,需要保存的轨迹就是变量 product 和 counter 的当前值,我们称这个过程为迭代计算过程。迭代计算过程就是那种其状态可以用固定数目的状态变量描述的计算过程,同时又存在一套固定的规则描述了计算过程从一个状态到另一个状态转换时状态变量的更新方式,还有一个结束状态的检测用以描述计算过程如何终止。

(factorial-iter   1 1 5)
(factorial-iter   1 2 5)
(factorial-iter   2 3 5)
(factorial-iter   6 4 5)
(factorial-iter  24 5 5)
(factorial-iter 120 6 5)
120

 

示例:斐波那契数列

Fibonacci(0)=0,

Fibonacci(1)=1,

Fibonacci(N)=Fibonacci(N−1)+Fibonacci(N−2)

可以看出斐波那契数列是一个天然递归的函数,函数递归在之前的代码中已经遇到了,直接看代码实现。

(define (fibonacci n)
  (cond ((= n 0) 0)
        ((= n 1) 1)
        (else (+ (fibonacci (- n 1))
                 (fibonacci (- n 2))))))

如果将 fibonacci 函数的调用图画出来,可以看到它就像一棵树一样,这样的递归被称为 树形递归。但是其中有大量的重复计算,会导致无意义的计算从而浪费 CPU 的性能。

 

由于这种计算斐波那契数列的方法很糟糕,做了很多冗余计算,其递归次数跟随 n 的大小指数增加,因此我们需要使用迭代的方法来优化这个求解过程。

(define (fibonacci n)
  (let fibonacci-iter ((a 1) (b 0) (counter 1))
    (if (> counter n)
        b
        (fibonacci-iter (+ a b) a (+ counter 1)))))

 树形递归计算过程并不是无用的,当考虑在层次结构性的数据上操作,而不是对数操作时,树形递归计算过程是一种自然、威力强大的工具,可以帮助我们理解与设计程序。

示例:判断奇偶数

如果不能直接使用取模的方法判断奇偶数,那么有一个简单且明了的方式 – 询问前一个数是否是奇 / 偶数。显然这是个递归问题,我们需要不断向前询问,直到得到答案。

(define (odd? n)
  (if (= n 0)
      #f
      (even? (- n 1))))
(define (even? n)
  (if (= n 0)
      #t
      (odd? (- n 1))))

 这种有多个函数互相递归调用的方式,称其为间接递归。

高阶函数

高阶函数 (Higher-Order Functions) 是一类将函数作为参数、返回值进行传递的函数,这种特性多发生在具有 FP 特性的语言中,往往这些语言还会同时提供 lambda。

lambda 表达式

 lambda 表达式 是一种简化的定义函数的方式,可以捕获当前环境中的一些变量,也被称为闭包 (clause)。lambda 往往伴随着高阶函数出现,通常是传递条件谓词时,创建一个对应的 lambda 对象,而不是创建一个函数传递。

(lambda (x)
  (= x 0))

lambda 与正常函数无异,由三部分组成 – 标头 (lambda 关键字)、参数列表函数体组成。另外一点,lambda 无法递归,如果想要递归 lambda 需要使用 Y 组合子。1 , 2

lambda 中捕获的环境变量是可以直接使用的

(define (zero? x)
  ((lambda () (= x 0))))
(zero? 0) ;; #t
(zero? 1) ;; #f

设计函数

对于一个函数,在设计一个函数时,需要注意三个方面:

  1. 每个函数应该只有一个精确的任务,执行多个任务的函数应该被拆分为多个函数
  2. 不要重复自己 (DRY, Don’t repeat yourself),如果你发现自己在复制粘贴一段代码,你可能已经发现了一个机会用函数抽象
  3. 函数应该被设计的更通用,比如不提供 square 和 cube,而是提供 pow,指定幂来分别实现 square 和 cube

示例:累加计算

比如你现在需要计算累和,包括但不限于 \sum_{i=1}^{n}{i}\sum_{i=1}^{n}{i^{2}} 等,根据设计函数的 3 个方面,我们需要设计一个用于累加的函数!另外,这个函数需要有足够的抽象,来提供泛用性。

那么我可以定义两个参数 startend 用于标识累加函数的上下限,那么最重要的如何累加应该怎么告诉这个函数呢?将这个函数设计为高阶函数!

 

(define (summation start end term)
  (let summation-iter ((counter start) (value 0))
    (if (> counter end)
        value
        (summation-iter (+ counter 1) (+ value (term counter))))))
(summation 0 10 (lambda (x) x))  ;; sum (i), 55
(summation 0 10 (lambda (x) (* x x))) ;; sum (i^2), 385
(summation 0 10 (lambda (x) (sqrt x))) ;; sum (sqrt(i)), 22.4682

示例:柯里化

柯里化 (currying) 是数学和 FP 的重要特性,将接收多个参数的函数转换为接收一个参数的函数,并且返回接收余下的参数而且返回结果的新函数的技术。所以这三个表达式是等价的。

 

(define (sum a b) (+ a b))
(define (sum-curry a) (lambda (b) (+ a b)))
(define add10 (sum-curry 10))
(add10 5)  ;; 15
(sum 10 5) ;; 15

Lab 1: Functions, Control

(define (falling n k)
  "Compute the falling factorial of n to depth k."
  (define factorial
    (lambda (n)
      (cond ((= n 0) 1)
            ((= n 1) 1)
            (else (* n (factorial (- n 1)))))))
  (/ (factorial n)
     (factorial (- n k))))

 

(define (sum-digits n)
  "Sum all the digits of y."
  (let sum-digits-iter ((num n) (val 0))
    (if (= num 0)
        val
        (sum-digits-iter (quotient num 10) (+ val (remainder num 10))))))

(define (double-eights n)
  "Return true if n has two eights in a row."
  (let double-eights-iter ((num n) (prev #f))
    (if (= num 0)
        #f
        (let ((curr (= (remainder num 10) 8)))
          (or (and curr prev)
              (double-eights-iter (quotient num 10) curr))))))

Homework 2: Higher Order Functions

product

         计算 term(1) \times term(2) \times \cdots \times term(n)

(define (product n term)
  "Return the product of the first n terms in a sequence."
  (let product-iter ((counter 1) (init 1))
    (if (> counter n)
        init
        (product-iter (+ counter 1) (* init (term counter))))))

 

accumulate

         累加函数        

(define (accumulate merger init n term)
  "Return the result of merging the first n terms in a sequence and start.
    The terms to be merged are term(1), term(2), ..., term(n). merger is a
    two-argument commutative function."
  (let accumulate-iter ((counter 1) (value init))
    (if (> counter n)
        value
        (accumulate-iter (+ counter 1) (merger value (term counter))))))

 

Project 1: The Game of Hog

I know! I’ll use my Higher-order functions to Order higher rolls.

在 Hog 中,两个玩家轮流尝试接近目标,成为第一个总分至少到达目标的玩家胜利,默认目标为 100 分。每次尝试,玩家选择至多十个色子进行投掷。玩家的得分是本轮所有骰子点数之和。一名玩家如果投掷太多骰子将会面临一定风险:

  • Sow Sad,如果任何骰子的结果为 1,则当前玩家的回合得分为 1。

在一局正常的 Hog 游戏中,这就是所有的规则了。为了增加一些游戏特色,我们将添加一些特殊规则:

  • Pig Tail,一名玩家如果选择投掷 0 个骰子,他将得 2 \times \lvert{tens - ones}\rvert + 1 分。其中 tensones 指对手分数的的十位数字和个位数字。

  • Square Swine,当一名玩家在他的回合获得了分数,且最终结果是一个完全平方数,那么将他的分数设置为下一个完全平方数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/131024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

个人能用的短信平台有哪些?看这一篇就够了

对于程序员个人来说&#xff0c;在做开发或者是接项目的时候&#xff0c;常常会用到发送短信功能模块&#xff0c;而自己写这个模块会要相当多的精力和时间&#xff0c;去找短信平台来解决问题&#xff0c;已经成了不少程序员的共识。 但市面上的短信平台确实很杂&#xff0c;鱼…

服务注册与发现原理

一、什么是服务注册与发现&#xff1f; 服务注册与发现就是一套管理微服务的组件&#xff0c;方便各拆分的服务平滑上线和下线&#xff0c;以及某个服务出现故障自动剔除。其实质就是维护一张记录各在线服务的表。让消费服知道服务在线可以请求。。。。。。 二、服务注册与发现…

SQL限制

SQL限制目录概述需求&#xff1a;设计思路实现思路分析1.URL管理参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,challenge Survive. happy…

PLL实验

PLL实验 开发板上面只有一个200MHz时钟输入&#xff0c;对于其它频率的时钟怎么办&#xff1f; 介绍 其实在很多 FPGA 芯片内部都集成了 PLL &#xff0c;其他厂商可能不叫 PLL &#xff0c;但是也有类似的功能模块&#xff0c;通过 PLL 可以倍频分频&#xff0c;产生其他很…

pytorch搭建yolov3网络

yolov3的整体网络结构 主要包含了两个部分。左边的Darknet-53主干特征提取网络主要用于提取特征。右边是一个FPN金字塔结构。 主干特征提取网络&#xff08;提取特征&#xff09; import math from collections import OrderedDict import torch.nn as nn#------------------…

MySQL事务相关知识

实践阅读&#xff1a;一文彻底读懂MySQL事务的四大隔离级别 1、什么是事务&#xff1f; 数据库事务&#xff08;简称&#xff1a;事务&#xff09;&#xff0c;是数据库管理系统执行过程中的一个逻辑单位&#xff0c;由一个有限的数据库操作序列构成&#xff0c;这些操作要么全…

AcWing 4645. 选数异或(预处理小区间 + 二分优化找)

题目如下&#xff1a; 题解 or 思路 在 [l,r][l, r][l,r] 区间内找到两个数 a,ba, ba,b 使得 a ^ b x 通过异或的性质&#xff1a; a ^ b ^ b x ^ b > a x ^ b 我们可以通过 b 可以迅速找到 a, a x ^ b 我们可以现预处理出所有小区间(大区间可以不需要&#xff0c;因为…

win11怎么分回c盘空间

有网友反映自己的win11系统有多个分区&#xff0c;c盘分区的空间比较小&#xff0c;便想通过win11分盘功能&#xff0c;将临近的磁盘空间分给c盘。那么win11怎么分回c盘空间&#xff1f;下面就教下大家win11将其他盘空间分回c盘的方法步骤。 工具/原料&#xff1a; 系统版本&am…

最近几年-基于深度学习自然语言处理的推荐系统-必读论文整理分享

基于内容的推荐&#xff08;Content-based Recommendation&#xff09;是信息过滤技术的延续与发展&#xff0c;它是建立在项目的内容信息上作出推荐的&#xff0c;而不需要依据用户对项目的评价意见&#xff0c;更多地需要用机器学习的方法从关于内容的特征描述的事例中得到用…

02-JVM堆里面的分区:Eden,survival (from+ to),老年代,各自的特点?

1.模型 堆里面分为新生代和老生代&#xff08;java8 取消了永久代&#xff0c;采用了 Metaspace&#xff09;&#xff0c;新生代包含 EdenSurvivor 区&#xff0c;survivor 区里面分为 from 和 to 区. 内存回收时&#xff0c;如果用的是复制算法&#xff0c;从 from 复制到 to…

项目实战之旅游网(十三)前台产品详情 前台收藏

目录 一.前台产品详情 1.查询产品 2.收藏按钮 二.前台收藏 本章的代码会统一放到最后。 一.前台产品详情 1.查询产品 即点击“查看详情”即可得到这个产品的详细描述。 并且项目运行起来之后&#xff0c;可以在后台新建新的产品&#xff0c;在前台可以实时查询到。 2.…

C工程与寄存器封装

目录 一、C语言工程简介 二、启动代码分析 三、C语言实现LED 四、寄存器的封装方式 五、寄存器操作的标准化 六、流水灯 一、C语言工程简介 先将工程模板解压 include里是.h文件 src里是.c文件 start里面是.s启动文件 ## NAME interface CROSS_COMPILE arm-no…

Fusion360 外壳开孔零件居中对齐的方法

DIY时需要根据从市场上采购的成品盒子进行开孔&#xff0c;通过3维软件进行开孔设计是比较方便的。Solidworks由于版权问题不易采用&#xff0c;Fusion360的个人版由于软件差异不像Solidworks方便约束定位。我通过尝试找到了适用方法。 先看成果。在一块板上居中位置开孔以便安…

电子学会2020年9月青少年软件编程(图形化)等级考试试卷(三级)答案解析

目录 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 二、判断题&#xff08;共10题&#xff0c;每题2分&#xff0c;共20分&#xff09; 三、编程题【该题由测评师线下评分】&#xff08;共3题&#xff0c;共30分&#xff09; 青少年软件…

阶段性回顾(4)与四道题目

TIPS 1. 当创建数组的时候&#xff0c;如果没有指定数组的元素个数也没关系&#xff0c;编译器会根据你初始化的内容来自行确定。 2. 在逻辑表达式当中&#xff0c;如果为真&#xff0c;就以1作为结果&#xff0c;如果为假&#xff0c;就以0作为结果。 3. 输入字符串的时候&…

VUE实例的组件的生命周期和数据共享和ref的使用方法(建议直接看ref)

目录VUE实例的组件的生命周期和数据共享组件间的数据共享ref的使用方法使用ref操作dom使用ref引用组件this.$nextTick(cb)方法的使用VUE实例的组件的生命周期和数据共享 生命周期和生命周期函数 这个是我学校vue书籍阿里云盘地址,有需要可以点击看看 生命周期&#xff08;Life…

对Java中异常的认识

文章目录一、异常的概念与体系结构1.异常的概念2.异常的体系结构3.异常的分类二、异常的处理1.防御式编程2. 异常的抛出3.异常的捕获4.异常处理流程总结三、自定义异常类一、异常的概念与体系结构 1.异常的概念 在生活中&#xff0c;当我们发现朋友表现出不舒服的情况&#x…

LabVIEW ​​单​进程​共享​变量​与​​全局​变量

LabVIEW ​​单​进程​共享​变量​与​​全局​变量 单​进程​共享​变量​与​LabVIEW​全局​变量​相似。​事实​上&#xff0c;​单​进程​共享​变量​的​实现​是在​LabVIEW​全局​变量​的​基础​上​增加​了​时间​标识​功能。 ​为了​比较​单​进程​…

《终身成长》读书笔记

文章目录书籍信息思维模式思维模式解析关于能力和成就的真相体育&#xff1a;冠军的思维模式商业&#xff1a;思维模式和领导力人际关系&#xff1a;关于相处的思维模式父母、老师与教练&#xff1a;思维模式的传播改变思维模式书籍信息 书名&#xff1a;《终身成长》 作者&am…

Java算法_LeetCode26:删除排序数组中的重复项

LeetCode26&#xff1a;删除排序数组中的重复项 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。 由于在某些语言中不能改变数组的长度&a…