用python和HY(lisp)代码为例学习什么是递归?

news2024/11/18 18:35:36

什么是递归?

看ANSI Common Lisp手册,里面提到递归第二章:欢迎来到 Lisp — ANSI Common Lisp 中文版,说:不要把递归看作一个普通函数来理解,因为普通函数经常被当成一个“机器”,原料从入口进,从出口出,但是对递归就有点难理解,因为这个机器又调用了自己.....所以要把递归想像成一个处理过程,不管是“机器”还是函数,它都只是针对的输入物料或信息的一种处理过程。

好吧,我刚看到原话的时候有种醍醐灌顶的感觉,但是请原谅我贫瘠的语言无法把它准确描述出来。我可以举个例子,比如查字典,它的处理过程就是“先按照某种规则翻到某一页,在这一页里面查看是否有要查找的‘字’,如果有,查找结束,如果没有,那就继续这个处理过程” 。当然这个翻页规则可以有多种,比如从前向后翻,比如从后向前翻,比如每次选取页数的1/2 来说翻。

我将使用python和HY(lisp)源代码的方式来辅助自己的理解,也希望能对大家有所帮助。

ps,不要有负担,其实递归真的很简单,我每隔一段时间就会弄明白一次^-^。

设定任务查字典

结合对“处理过程”的理解,我们设定了一个简化的查字典的任务,即给出一个字符(也可以是字符串)和一个包含字符(串)的列表,从列表中找出匹配项,并把匹配项后面的元素一起打出来。

还可以增加倒查模式,即查到后把匹配项前面的元素一起打出来。

比如给出x = "c" ; ListB = ["a", "b", "c", "d"] ,那么从左边查找的返回值是["c", "d"],从右边查找的返回值是 ["a", "b", "c"]

编码实现查字符串

首先学习一下HY

HY的语法

不要有负担,HY语言不会也没有关系,看完有个印象就行。毕竟“处理过程”是LISP书里提到的,我们用HY lisp语言跑通例子会更优仪式感^-^。

HyPython

(setv foobar (+ 2 2))

(setv [tim eric] ["jim" "derrick"])

(setv alpha "a" beta "b")

foobar = 2 + 2

tim, eric = 'jim', 'derrick'

alpha = 'a'; beta = 'b'

(sorted "abcBC"

  :key (fn [x] (.lower x)))

sorted("abcBC",

    key = lambda x: x.lower())

(defn test [a b [c "x"] #* d]

  [a b c d])

def test(a, b, c="x", *d):

    return [a, b, c, d]

(with [o (open "file.txt" "rt")]

  (setv buffer [])

  (while (< (len buffer) 10)

    (.append buffer (next o))))

with open('file.txt', 'rt') as o:

    buffer = []

    while len(buffer) < 10: 

        buffer.append(next(o))

(lfor

  x (range 3)

  y (range 3)

  :if (= (+ x y) 3) (* x y))

[x * y

    for x in range(3)

    for y in range(3)

    if x + y == 3]

(defmacro do-while [test #* body]

  `(do

  ~@body

  (while ~test

    ~@body)))

(setv x 0)

(do-while x

  (print "Printed once."))

x = 0

print("Printed once.")

while x:

    print("Printed once.")

HY的安装

在python环境下,直接使用pip安装即可

pip install hy

安装完成后,命令行模式下直接键入“hy”即可进入hy的交互界面

hy
Hy 0.29.0 using CPython(main) 3.10.13 on FreeBSD
=> (print "hello world")
hello world

pyhton实现

任务需求:给出x = "c" ; ListB = ["a", "b", "c", "d"] ,那么从左边查找的返回值是["c", "d"],从右边查找的返回值是 ["a", "b", "c"]

python代码如下:

# 学习递归 从右边查找匹配项
def thirdright(x, ListA):
    if not ListA :
        return None
    else:
        if x == ListA[-1]:
            return ListA
        else:
            return thirdright(x, ListA[:-1])

# 学习递归 从左边查找匹配项
def thirdleft(x, ListA):
    if not ListA :
        return None
    else:
        if x == ListA[0]:
            return ListA
        else:
            return thirdleft(x, ListA[1:])

运行测试

x = "c" ; ListB = ["a", "b", "c", "d"]
y = thirdleft(x, ListB)
z = thirdright(x, ListB)
print(y,z)

输出结果:

['c', 'd'] ['a', 'b', 'c']

HY lisp代码实现

任务需求:给出x = "c" ; ListB = ["a", "b", "c", "d"] ,那么从左边查找的返回值是["c", "d"],从右边查找的返回值是 ["a", "b", "c"]

当然在HY里,赋值语句是(setv x "c") (setv ListB ["a" "b" "c" "d"])

从左向右查找HY代码:

(defn searchleft [x ListA] 
(if (= ListA [])
  None
  (if (=  (get ListA 0) x)
    ListA 
    (searchleft x  (cut ListA 1 100)))))

(setv x "c") (setv ListB ["a" "b" "c" "d"])
(searchleft x ListB)

输出

["c" "d"]
 

从左向右查找HY代码:

(defn searchright [x ListA] 
(if (= ListA [])
  None
  (if (=  (get ListA -1) x)
    ListA 
    (searchright x  (cut ListA 0 -1)))))

(setv x "c") (setv ListB ["a" "b" "c" "d"])
(searchright x ListB)

输出

=> (searchright x ListB)
["a" "b" "c"]

HY lisp代码确实完成了功能需求,除了有太多右括号“)))))”,程序代码整体看着还是挺清爽的。

HY里面没有常规lisp语言里的cdr和car两条指令,所以这里用了get 和cut两个函数,感觉不如cdr和car丝滑。

递归深度限制和尾递归

python不支持尾递归,且有递归深度限制

def recursion(n):
    if n==1:
        return n
    else:
        return n+recursion(n-1)  

最大递归深度1000,所以数大点就报错了

recursion(1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in recursion
  File "<stdin>", line 5, in recursion
  File "<stdin>", line 5, in recursion
  [Previous line repeated 995 more times]
  File "<stdin>", line 2, in recursion
RecursionError: maximum recursion depth exceeded in comparison

 HY是基于python的,所以也有最大递归深度限制

(defn recursion [x]  
  (if (= x 1)  
    1  
    (+ x (recursion (- x 1)))))

(recursion 1000)

报错:RecursionError: maximum recursion depth exceeded in comparison

hy的尾递归代码没调通。
 

common Lisp的代码

(defun recursion (x)
  (if (= x 1)  
    1  
    (+ x (recursion (- x 1)))))

3000的时候没问题,到3400的也是报错

(recursion 3400)

*** - Lisp stack overflow. RESET
 

sbcl尾递归

目前只有sbcl支持尾递归

(defun recursion-tail (x &optional (sum 0))  
  (if (= x 1)  
      (+ sum 1)  
      (recursion-tail (- x 1) (+ sum x))))  
  
; 调用函数  
(recursion-tail 1000)

 在sbcl的尾递归里,可以轻松到五百万深度

* (recursion-tail 5000000)
12500002500000

证明尾递归优化确实了不起!

总结

递归就是行为规则,是一种处理过程,递归也是最符合人类原生行为的编程方法,所以即使很复杂的问题,也比较容易用递归来实现算法。比如汉诺塔,使用递归代码非常简洁,使用循环则代码复杂很多。

关于递归的资源消耗,确实要比普通函数多,不过很多编译器专门对递归进行了优化,许多Common Lisp 编译器(比如sbcl)都可以把尾递归转化成循环函数,这样就没有额外的资源消耗了。

再回过头来看看ANSI Common Lisp关于递归的描述,加强一下记忆

起初,许多人觉得递归函数很难理解。大部分的理解难处,来自于对函数使用了错误的比喻。人们倾向于把函数理解为某种机器。原物料像实参一样抵达;某些工作委派给其它函数;最后组装起来的成品,被作为返回值运送出去。如果我们用这种比喻来理解函数,那递归就自相矛盾了。机器怎可以把工作委派给自己?它已经在忙碌中了。

较好的比喻是,把函数想成一个处理的过程。在过程里,递归是在自然不过的事情了。日常生活中我们经常看到递归的过程。举例来说,假设一个历史学家,对欧洲历史上的人口变化感兴趣。研究文献的过程很可能是:

  1. 取得一个文献的复本
  2. 寻找关于人口变化的资讯
  3. 如果这份文献提到其它可能有用的文献,研究它们。

过程是很容易理解的,而且它是递归的,因为第三个步骤可能带出一个或多个同样的过程。

所以,别把 our-member 想成是一种测试某个东西是否为列表成员的机器。而是把它想成是,决定某个东西是否为列表成员的规则。如果我们从这个角度来考虑函数,那么递归的矛盾就不复存在了。

调试 

hy里没有切片

=> ["a" "b"][0]
[0]
=> ["a" "b"][0:1]
Traceback (most recent call last):
  File "stdin-ff769649939a083bf75b8d8e4be71e7a4f33d180", line 1, in <module>
    ["a" "b"][0:1]
NameError: name 'hyx_0XcolonX1' is not defined
可以用cut代替

=> (cut [1 2] 0)
[]
=> (cut [1 2 3] 1)
[1]
=> (cut [1 2 3] 2)
[1 2]
=> (cut [1 2 3] -1)
[1 2]
如果读取某个元素,更适合的是get

=> ListB
["a" "b" "c" "d"]
=> (get ListB 0)
"a"
=> (get ListB 1)
"b"

hy源码:GitHub - hylang/hy: A dialect of Lisp that's embedded in Python

手册:Contents — Hy 0.29.0 manual 

指引:Tutorial — Hy 0.29.0 manual 

代码报错parse error for pattern macro 'if': got unexpected token: 

...   (+ x (recursion (- x 1)))))
Traceback (most recent call last):
  File "stdin-9598c437ea17b110b2200908cd5c44900ea2edef", line 4
    (+ x (recursion (- x 1)))))
    ^
hy.errors.HySyntaxError: parse error for pattern macro 'if': got unexpected token: hy.models.Expression([
  hy.models.Symbol('+'),
  hy.models.Symbol('x'),
  hy.models.Expression([
    hy.models.Symbol('recursion'),
    hy.models.Expression([
      hy.models.Symbol('-'),
      hy.models.Symbol('x'),
      hy.models.Integer(1)])])]), expected: end of macro call

python递归报错

>>> recursion(1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in recursion
  File "<stdin>", line 5, in recursion
  File "<stdin>", line 5, in recursion
  [Previous line repeated 995 more times]
  File "<stdin>", line 2, in recursion
RecursionError: maximum recursion depth exceeded in comparison

python有最大递归深度限制,最大1000

import sys

sys.getrecursionlimit()
1000
可以使用sys.setrecursionlimit进行设置。

hy尾递归代码报错

 (defn recursion-tail [x &optional [sum 0]]  
...     (if (= x 1)  
...         (+ sum 1)  
...         (recursion-tail (- x 1) (+ sum x))))  
=> (recursion-tail 1000)
Traceback (most recent call last):
  File "stdin-db38a56499b0c167c6ac4f20a4f89229b594a496", line 1, in <module>
    (recursion-tail 1000)
TypeError: recursion_tail() missing 1 required positional argument: 'hyx_XampersandXoptional'

先搁置

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

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

相关文章

2021数学建模A题目–“FAST”主动反射面的形状调节

A 题——“FAST”主动反射面的形状调节 思路&#xff1a;该题主要是通过利用伸缩杆调整FAST反射面&#xff0c;给出合适的调整方案 程序获取 第一题问题思路与结果&#xff1a; 当待观测天体S位于基准球面正上方&#xff0c;结合考虑反射面板调节因素&#xff0c;确定理想抛物…

消息队列的对比及适配的应用场景

消息队列的对比及适配的应用场景## 特性 / 消息队列KafkaRabbitMQActiveMQRedis消息模型发布-订阅、流处理队列、发布-订阅队列、发布-订阅发布-订阅协议支持自定义TCP协议、REST代理AMQP、STOMP、MQTTAMQP、OpenWire、STOMP、MQTT自定义协议可用性非常高&#xff0c;分区和副…

域权限维持之伪造域控

2022年1月10日&#xff0c;国外安全研究员Kaido发文称发现了一种新的伪造域控方式&#xff0c;安全研究员只需要新建一个机器账户&#xff0c;然后修改机器账户的UserAccountControl属性为8192。活动目录就会认为这个机器账户就是域控&#xff0c;然后就可以使用这个新建的机器…

STM32多功能交通灯系统:从原理到实现

一、功能说明 本交通灯系统采用先进的stm32f103c8t6微处理器为核心控制单元。系统设置东南西北四个方向各配置两位数码管&#xff0c;用以精准展示5至99秒的时间范围&#xff0c;并且允许用户根据实际需求进行灵活调整。 在信号灯配置方面&#xff0c;每个方向均配备左转、直…

在进行JD(京东)电商API大数据采集,针对商品详情数据、SKU数据以及价格分析时,关键是数据的准确性、完整性和分析的深度

一、项目背景 网上购物已经成为大众生活的重要组成部分。人们在电商平台上浏览商品并购物&#xff0c;产生了海量的用户行为数据&#xff0c;用户对商品的评论数据对商家具有重要的意义。利用好这些碎片化、非结构化的数据&#xff0c;将有利于企业在电商平台上的持续发展&…

已成功见刊检索的国际学术会议论文海报展示(2)

【先投稿先送审】第四届计算机、物联网与控制工程国际学术会议&#xff08;CITCE 2024) 大会官网&#xff1a;www.citce.org 时间地点&#xff1a;2024年11月1-3日&#xff0c;中国-武汉 收录检索&#xff1a;EI Compendex&#xff0c;Scopus 主办单位&#xff1a;四川师范…

独立农作物区域-第13届蓝桥杯省赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第86讲。 独立农作物区域&a…

食品企业仓储式批发零售一体化解决方案

食品企业需要有效应对日益复杂的市场挑战和消费者需求的快速变化的挑战并提升市场竞争力&#xff0c;仓储式类的批发零售一体化需求应运而生。这一全新的商业模式不仅整合了传统的批发和零售模式&#xff0c;还优化了供应链管理和客户体验&#xff0c;成为食品行业发展的新引擎…

docker安装Jumpserver

docker安装Jumpserver 简介 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。 JumpServer 堡垒机帮助企业以更安全的方式管控和登录各种类型的资产&#xff0c;包括&#xff1a; SSH: Linux / Unix / 网络设备 等&#xff1b; Windows:…

中科数安 |-透明加密软件_无感透明加密 - 源头有保障

中科数安的透明加密软件是一款专为保护企业数据安全而设计的高级产品&#xff0c;它采用了无感透明加密技术&#xff0c;确保源头数据的安全可靠。 ——www.weaem.com 以下是该软件的主要特点和功能概述&#xff1a; 无感透明加密&#xff1a; 中科数安的透明加密软件能够在用…

分类预测 | Matlab实现GA-XGBoost遗传算法优化XGBoost的多特征分类预测

分类预测 | Matlab实现GA-XGBoost遗传算法优化XGBoost的多特征分类预测 目录 分类预测 | Matlab实现GA-XGBoost遗传算法优化XGBoost的多特征分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现GA-XGBoost遗传算法优化XGBoost的多特征分类预测&#xff0c;…

Spring Boot集成Minio插件快速入门

1 Minio介绍 MinIO 是一个基于 Apache License v2.0 开源协议的对象存储服务。它兼容亚马逊 S3 云存储服务接口&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等&#xff0c;而一个对象文件可以是任意大小&…

【机器学习300问】125、什么是双向循环神经网络(BRNN)?什么是深度循环神经网络(DRNN)?

一、双向循环神经网络 &#xff08;1&#xff09;诞生背景 双向循环神经网络&#xff08;Bidirectional Recurrenct Neural Network, BRNN&#xff09;是在深度学习领域发展起来的一种特殊类型的循环神经网络&#xff08;RNN&#xff09;&#xff0c;它诞生的背景是为了解决传…

抽卡机小程序:设计与开发全攻略

在移动互联网时代&#xff0c;小程序以其轻便、易用、无需安装的特点&#xff0c;迅速成为用户日常使用的重要工具。其中&#xff0c;抽卡机小程序因其独特的娱乐性和互动性&#xff0c;受到广大用户的喜爱。本文将为大家详细介绍抽卡机小程序的设计与开发全攻略。 一、需求分析…

电子竞赛5——作息时间控制器

一 . 题目要求 用单片机制作作息时间控制器&#xff1b;用四位数码管显示实时时钟&#xff08;时、分&#xff0c;24小时制、12小时制&#xff09;&#xff0c;有秒闪&#xff0c;小时十位有零消隐&#xff1b;可用数字键或、-键校时&#xff08;可快速、-&#xff09;被校位&…

通过OOS定时升级EIP实例临时带宽

目录 功能背景 关键特性 应用场景 使用限制 操作步骤 附录 执行流程图 模板 功能背景 随着业务的不断发展和互联网应用场景的多样化&#xff0c;企业或个人用户在特定时间段内面临网络流量剧增的挑战变得尤为常见。这些流量高峰&#xff0c;如大规模促销活动、热门直播…

[递归与栈]The Sierpinski Fractal

描述 Consider a regular triangular area, divide it into four equal triangles of half height and remove the one in the middle. Apply the same operation recursively to each of the three remaining triangles. If we repeated this procedure infinite times, wed …

我劝你别惹“女”项目经理

她来了她来了&#xff0c;她带着项目进度走来了&#xff01;&#xff01;&#xff01; 在职场的江湖里&#xff0c;流传着一个传说&#xff0c;那便是“女”项目经理的神话。她们&#xff0c;是团队中的“铁娘子”&#xff0c;是项目里的“指挥官”&#xff0c;更是无数人心中的…

商城小程序:颠覆传统电商,打造全新商业生态

在数字化浪潮的推动下&#xff0c;网购行业呈现多元化繁荣发展态势&#xff0c;出现了琳琅满目的商品应用小程序&#xff0c;但市面上的商城小程序基本属于通用型&#xff0c;无论是商城界面展示和基本功能&#xff0c;都不能满足个性化和商品推广需求&#xff0c;阻碍了商品的…

故障记录---docker僵死

故障现象&#xff1a; docker进程僵死&#xff0c;docker命令无法使用 处理过程&#xff1a; 查看docker进程状态&#xff0c;显示句柄数过多&#xff0c;于是重启了docker [rootdata02 opt]# systemctl status docker ● docker.service - Docker Application Container Eng…