造个轮子--用Python写个编程语言-函数与基本数据结构实现

news2024/11/26 2:36:12

前言

okey,经过一段时间的努力,接下来要实现的是函数。当然还有对应的基本数据结构,那么之后的话,我们的工作就开始进一步转换了。

那么在这块我们要实现的有:

  1. 函数的定义
  2. String类型的实现
  3. 列表类型的实现

实话实话,这个的实现是相当简陋的。不过作为一个小模型,应该是够用了,后面可以尝试打磨一下这个细节,比如这个变量的管理,函数的管理等等。

那么这块先看到我们的实现效果: 在这里插入图片描述

okey,我们开始吧。

语法表述

okey,现在的话,想要实现这个的话,我们需要先来看到我们的这个语法表述,前面我们一直在强调AST,因为这个玩意决定了,我们要如何愉快玩耍。

那么现在的话,我们来看到这个描述:

expr	    : KEYWORD:VAR IDENTIFIER EQ expr
		    : comp-expr ((KEYWORD:AND|KEYWORD:OR) comp-expr)*

comp-expr	: NOT comp-expr
			: arith-expr ((EE|LT|GT|LTE|GTE) arith-expr)*

arith-expr	: term ((PLUS|MINUS) term)*

term		: factor ((MUL|DIV) factor)*

factor		: (PLUS|MINUS) factor
			: power

power		: call (POW factor)*

call		: atom (LPAREN (expr (COMMA expr)*)? RPAREN)?

atom 		: INT|FLOAT|STRING|IDENTIFIER
			: LPAREN expr RPAREN
			: if-expr
			: for-expr
			: while-expr
			: func-def

if-expr		: KEYWORD:IF expr KEYWORD:THEN expr
			                (KEYWORD:ELIF expr KEYWORD:THEN expr)*
			                (KEYWORD:ELSE expr)?

for-expr	: KEYWORD:FOR IDENTIFIER EQ expr KEYWORD:TO expr
							(KEYWORD:STEP expr)? KEYWORD:THEN expr

while-expr	: KEYWORD:WHILE expr KEYWORD:THEN expr

func-def	: KEYWORD:FUN IDENTIFIER?
							LPAREN (IDENTIFIER (COMMA IDENTIFIER)*)? RPAREN
							ARROW expr

前面的都是老朋友,我们主要看到这个call,和func-def. 这两个家伙就是我们这个函数部分的操作。 这个和我们的变量实现一样,其实分两个部分,一个是函数本身的定义 例如Python的函数定义:

def add(a,b):

然后的话,是我们对函数的调用

add(a,b)

这里的话,其实是一样的。

所以的话,定义了这两个东西。

之后的话,我们来看到完整的描述:

  1. expr:表示一个表达式。它可以是一个变量赋值表达式(以关键字 ‘VAR’ 开头),或者是一个比较表达式 comp-expr,多个比较表达式之间可以使用逻辑运算符 ‘AND’ 和 ‘OR’ 进行连接。

  2. comp-expr:表示一个比较表达式。它可以是一个逻辑非表达式(以关键字 ‘NOT’ 开头),或者是一个算术表达式 arith-expr,多个算术表达式之间可以使用比较运算符进行比较。

  3. arith-expr:表示一个算术表达式。它由一个项 term 开始,后面可以跟随多个加减运算符和项。

  4. term:表示一个项。它由一个因子 factor 开始,后面可以跟随多个乘除运算符和因子。

  5. factor:表示一个因子。它可以是正负号与另一个因子相乘的结果,或者是一个幂运算表达式 power

  6. power:表示一个幂运算表达式。它由一个函数调用 call 开始,后面可以跟随多个幂运算操作。

  7. call:表示一个函数调用。它由一个原子表达式 atom 开始,后面可以跟随一对括号和多个参数表达式。

  8. atom:表示一个原子表达式。它可以是整数、浮点数、字符串或标识符,也可以是一个被括号包裹的表达式,或者是一个 if 表达式、for 循环表达式、while 循环表达式或函数定义表达式。

  9. if-expr:表示一个 if 表达式。它以关键字 ‘IF’ 开头,后面跟随条件表达式、关键字 ‘THEN’ 和对应的表达式。之后可以有零个或多个关键字 ‘ELIF’、条件表达式和关键字 ‘THEN’ 和对应的表达式。最后可以有一个可选的关键字 ‘ELSE’ 和对应的表达式。

  10. for-expr:表示一个 for 循环表达式。它以关键字 ‘FOR’ 开头,后面跟随循环变量、赋值运算符、起始值、关键字 ‘TO’、结束值,然后可以是可选的关键字 ‘STEP’ 和步长值,最后是关键字 ‘THEN’ 和对应的表达式。

  11. while-expr:表示一个 while 循环表达式。它以关键字 ‘WHILE’ 开头,后面跟随条件表达式,然后是关键字 ‘THEN’ 和对应的表达式。

  12. func-def:表示一个函数定义表达式。它以关键字 ‘FUN’ 开头,后面可以是可选的标识符作为函数名称,然后是一对括号和多个参数标识符。最后是箭头符号和对应的表达式。

这样的话,就定义好了这个家伙。

解析器修改

okey,现在的话,开始来正式进入代码的编写部分。首先的话,毫无疑问的是,我们要做的是分为两个大部分,首先是对token词法解析的修改,然后是我们语法解析器的修改。当然这块的话我们就一起说了。

词法解析

首先,我们还是老规矩,去定义:


TT_INT = "整数"
TT_FLOAT = "浮点数"
TT_STRING = '字符串'
TT_PLUS = "加号"
TT_DIV = "除号"
TT_MINUS = "减号"
TT_LPAREN = "左括号"
TT_RPAREN = "右括号"
TT_POW	= '次幂'
TT_MUL = "乘"
TT_EOF = 'EOF'
TT_IDENTIFIER = '设变量'
TT_KEYWORD = '关键字'
TT_EQ = '赋值'
TT_EE = '等于'
TT_NE = '不等于'
TT_LT = '小于'
TT_GT = '大于'
TT_LTE = '小于等于'
TT_GTE = '大于等于'
TT_LSQUARE = '[括号'
TT_RSQUARE = ']括号'
TT_COMMA = ',号'
TT_ARROW = '-> 符号'

KEYWORDS = [
  'var',
  'and',
  'or',
  'not',
  'if',
  'elif',
  'else',
  'for',
  'to',
  'step',
  'while',
  'fun',
  'then'
]


可以看到,这个就是我们目前定义的关键字,还有对应的Token类型。

在解析器这里的话,我们要重新新增修改的代码不多,因为关键字匹配我们先前就是写好的,现在要做的只是匹配这个[]括号,主要是为了,处理这个列表变量。

当然还有这个: 在这里插入图片描述 这个家伙的话,可以去获取到我们的这个字符串,出现“ 这个符号的时候,我们就需要进行特殊处理了。

  def make_string(self):
        string = ''
        pos_start = self.pos.copy()
        escape_character = False
        self.advance()

        escape_characters = {
            'n': '\n',
            't': '\t'
        }

        while self.current_char != None and (self.current_char != '"' or escape_character):
            if escape_character:
                string += escape_characters.get(self.current_char, self.current_char)
            else:
                if self.current_char == '\\':
                    escape_character = True
                else:
                    string += self.current_char
            self.advance()
            escape_character = False

        self.advance()
        return Token(TT_STRING, string, pos_start, self.pos)

函数节点

那么同样的,我们要加入这个函数节点。 这里的话,我们有两个:

FuncDefNode 类表示函数定义的节点。它具有以下属性:

var_name_tok:函数名称的记号(Token)。
arg_name_toks:参数名称的记号列表。
body_node:函数体的节点。
pos_start:节点在源代码中的起始位置。如果存在函数名记号,
则起始位置为该记号的起始位置;如果不存在函数名记号但存在参数名记号,
则起始位置为第一个参数名记号的起始位置;否则起始位置为函数体节点的起始位置。
pos_end:节点在源代码中的结束位置,等于函数体节点的结束位置。

CallNode 类表示函数调用的节点。它具有以下属性:

node_to_call:被调用的函数节点。
arg_nodes:参数节点列表。
pos_start:节点在源代码中的起始位置,等于被调用函数节点的起始位置。
pos_end:节点在源代码中的结束位置。如果存在参数节点,
则结束位置为最后一个参数节点的结束位置;否则结束位置为被调用函数节点的结束位置。

class FuncDefNode:
  def __init__(self, var_name_tok, arg_name_toks, body_node):
    self.var_name_tok = var_name_tok
    self.arg_name_toks = arg_name_toks
    self.body_node = body_node

    if self.var_name_tok:
      self.pos_start = self.var_name_tok.pos_start
    elif len(self.arg_name_toks) > 0:
      self.pos_start = self.arg_name_toks[0].pos_start
    else:
      self.pos_start = self.body_node.pos_start

    self.pos_end = self.body_node.pos_end

class CallNode:
  def __init__(self, node_to_call, arg_nodes):
    self.node_to_call = node_to_call
    self.arg_nodes = arg_nodes

    self.pos_start = self.node_to_call.pos_start

    if len(self.arg_nodes) > 0:
      self.pos_end = self.arg_nodes[len(self.arg_nodes) - 1].pos_end
    else:
      self.pos_end = self.node_to_call.pos_en

函数节点解析

定义号函数节点之后的话,我们要做就是构建函数节点了,那么对于这个节点的处理,其实和我们的这个for循环,while 还有这个if的其实是类似的。 在这里插入图片描述

我们直接上代码吧,然后在注释里面说明:

    def func_def(self):
        res = ParseResult()
        # 当前节处理是函数节点,检查有没有fun关键字
        if not self.current_tok.matches(TT_KEYWORD, 'fun'):
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected 'fun'"
            ))
	
        res.register_advancement()
        self.advance()
		
		# 处理函数的形式变量,也就是参数
		# 问:为什么可以检查到下一个节点
		# 答,算过了(狗头) 好吧是adanven这个函数,它会滑动,
		# 这里面的话register其实是递归调用了,所以,前面结束之后,拿到的一定
		# 就是符合语法规范的下一个token
        if self.current_tok.type == TT_IDENTIFIER:
            var_name_tok = self.current_tok
            res.register_advancement()
            self.advance()
            #检查左括号
            if self.current_tok.type != TT_LPAREN:
                return res.failure(InvalidSyntaxError(
                    self.current_tok.pos_start, self.current_tok.pos_end,
                    f"Expected '('"
                ))
        #没有参数
        else:
            var_name_tok = None
            if self.current_tok.type != TT_LPAREN:
                return res.failure(InvalidSyntaxError(
                    self.current_tok.pos_start, self.current_tok.pos_end,
                    f"Expected identifier or '('"
                ))

        res.register_advancement()
        self.advance()
        arg_name_toks = []
		# 处理参数
        if self.current_tok.type == TT_IDENTIFIER:
            arg_name_toks.append(self.current_tok)
            res.register_advancement()
            self.advance()
			#下一个参数逗号隔开
            while self.current_tok.type == TT_COMMA:
                res.register_advancement()
                self.advance()

                if self.current_tok.type != TT_IDENTIFIER:
                    return res.failure(InvalidSyntaxError(
                        self.current_tok.pos_start, self.current_tok.pos_end,
                        f"Expected identifier"
                    ))

                arg_name_toks.append(self.current_tok)
                res.register_advancement()
                self.advance()

            if self.current_tok.type != TT_RPAREN:
                return res.failure(InvalidSyntaxError(
                    self.current_tok.pos_start, self.current_tok.pos_end,
                    f"Expected ',' or ')'"
                ))
        # 没有参数了
        else:
            if self.current_tok.type != TT_RPAREN:
                return res.failure(InvalidSyntaxError(
                    self.current_tok.pos_start, self.current_tok.pos_end,
                    f"Expected identifier or ')'"
                ))

        res.register_advancement()
        self.advance()
	
        if self.current_tok.type != TT_ARROW:
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected '->'"
            ))

        res.register_advancement()
        self.advance()
        node_to_return = res.register(self.expr())
        if res.error: return res
		# 完成参数节点定义,得到函数名,参数名(变量名)
        return res.success(FuncDefNode(
            var_name_tok,
            arg_name_toks,
            node_to_return
        ))


同样的,我们来看到call的实现。

    def call(self):
        res = ParseResult()
        atom = res.register(self.atom())
        if res.error: return res
		"""
		函数是可以有括号把里面的东西括起来的,就像这样:
		fun show()-> ( var b=3 )
		"""
        if self.current_tok.type == TT_LPAREN:
            res.register_advancement()
            self.advance()
            arg_nodes = []

            if self.current_tok.type == TT_RPAREN:
                res.register_advancement()
                self.advance()
            else:
                arg_nodes.append(res.register(self.expr()))
                if res.error:
                    return res.failure(InvalidSyntaxError(
                        self.current_tok.pos_start, self.current_tok.pos_end,
                        "Expected ')', 'var', 'if', 'for', 'while', 'fun', int, float, identifier, '+', '-', '(', '[' or 'not'"
                    ))

                while self.current_tok.type == TT_COMMA:
                    res.register_advancement()
                    self.advance()

                    arg_nodes.append(res.register(self.expr()))
                    if res.error: return res

                if self.current_tok.type != TT_RPAREN:
                    return res.failure(InvalidSyntaxError(
                        self.current_tok.pos_start, self.current_tok.pos_end,
                        f"Expected ',' or ')'"
                    ))

                res.register_advancement()
                self.advance()
             # 当前面的都执行完毕之后的话,我们的这个Call节点就ok了。
            return res.success(CallNode(atom, arg_nodes))
        return res.success(atom)

他们着两个的关系其实就是这样的: 在这里插入图片描述

List的解析实现

ok, 这里别忘了,我们还实现了我们的数据结构,String和List,所以这里的话,我们还要对这个两个家伙处理。不过这块的话,对于String的话其实没有什么特殊的,只需要解释的时候进行处理,但是对于List的话,因为这个家伙是有语法规则的,因此还需要单独处理。

 def list_expr(self):
        res = ParseResult()
        element_nodes = []
        pos_start = self.current_tok.pos_start.copy()

        if self.current_tok.type != TT_LSQUARE:
            return res.failure(InvalidSyntaxError(
                self.current_tok.pos_start, self.current_tok.pos_end,
                f"Expected '['"
            ))

        res.register_advancement()
        self.advance()

        if self.current_tok.type == TT_RSQUARE:
            res.register_advancement()
            self.advance()
        else:
            element_nodes.append(res.register(self.expr()))
            if res.error:
                return res.failure(InvalidSyntaxError(
                    self.current_tok.pos_start, self.current_tok.pos_end,
                    "Expected ']', 'var', 'if', 'for', 'while', 'fun', int, float, identifier, '+', '-', '(', '[' or 'not'"
                ))

            while self.current_tok.type == TT_COMMA:
                res.register_advancement()
                self.advance()

                element_nodes.append(res.register(self.expr()))
                if res.error: return res

            if self.current_tok.type != TT_RSQUARE:
                return res.failure(InvalidSyntaxError(
                    self.current_tok.pos_start, self.current_tok.pos_end,
                    f"Expected ',' or ']'"
                ))

            res.register_advancement()
            self.advance()

        return res.success(ListNode(
            element_nodes,
            pos_start,
            self.current_tok.pos_end.copy()
        ))


这里的话,我特意把注释删掉了,按照我们前面的套路,我想应该是可以把这个看明白的。

解释器

之后是我们的解释器。

节点

同样的在我们的解释器里面也有节点,这个节点,前面忘记说了,先前的话我只要Number这个节点,主要是因为当时我们只有这个对数学的基本运算,没有啥高级的操作,因此一个就够了,这个玩意就是用来存储运算结果的。

同样的,现在的话,操作复杂了,因此我们要做的事情就多了,所以的话,在这里我们有这些玩意:

image.png

(代码就不贴了,后面直接看到项目即可)

在这里:

Value类是所有其他类的基类,它定义了一些通用的方法和属性,例如set_pos()用于设置对象在源代码中的位置,set_context()用于设置对象的上下文环境。还有一些操作符方法,如added_to()、subbed_by()等,用于处理对象之间的加减乘除等操作。

String类继承自Value类,代表字符串类型的值。它重载了added_to()方法和multed_by()方法,实现了字符串的拼接和重复操作。is_true()方法判断字符串是否为真(即非空)。copy()方法用于创建String对象的副本。

List类也继承自Value类,代表列表类型的值。它重载了added_to()方法、subbed_by()方法、multed_by()方法和dived_by()方法,实现了列表的添加、删除、拼接和取元素操作。copy()方法用于创建List对象的副本。

Function类继承自Value类,代表函数类型的值。它包含函数的名称、函数体和参数名列表。execute()方法用于执行函数调用,将参数传递给函数,并在新的上下文环境中执行函数体。copy()方法用于创建Function对象的副本。

函数操作

我们先来看到我们的函数处理: 在我们的解释器部分有这个代码: 在这里插入图片描述 我们把这个函数相关的信息,进行组装,然后得到这个函数对象,然后通过函数对象进行操作。得到结果,或者运行函数里面的代码。 所以在这里的话,我们要重点看到这里,函数的执行代码:

可以看到,我们在这里的操作,是,我们得到了函数主体的入口节点,画图题可能是这样的: 在这里插入图片描述

我们的execute函数会拿到这个body,当然还有参数,然后我们让解释器,从这个body头节点开始执行

然后就是我们的局部参数,可以看到我们的代码在这里面:

        for i in range(len(args)):
            arg_name = self.arg_names[i]
            arg_value = args[i]
            arg_value.set_context(new_context)
            new_context.symbol_table.set(arg_name, arg_value)

我们把这个变量的值什么的都放在了函数内部的局部context里面,所以这个就是局部参数的由来,我们在这里面实现了变量的隔离。

okey,接下来我们看到这个完整代码:

    def execute(self, args):
        res = RTResult()
        interpreter = Interpreter()
        new_context = Context(self.name, self.context, self.pos_start)
        new_context.symbol_table = SymbolTable(new_context.parent.symbol_table)

        if len(args) > len(self.arg_names):
            return res.failure(RTError(
                self.pos_start, self.pos_end,
                f"{len(args) - len(self.arg_names)} too many args passed into '{self.name}'",
                self.context
            ))

        if len(args) < len(self.arg_names):
            return res.failure(RTError(
                self.pos_start, self.pos_end,
                f"{len(self.arg_names) - len(args)} too few args passed into '{self.name}'",
                self.context
            ))

        for i in range(len(args)):
            arg_name = self.arg_names[i]
            arg_value = args[i]
            arg_value.set_context(new_context)
            new_context.symbol_table.set(arg_name, arg_value)

        value = res.register(interpreter.visit(self.body_node, new_context))
        if res.error: return res
        return res.success(value)

String和List处理

接下来的话,就是String和List的处理,这里面的话没啥,直接看到代码就好了。

  def visit_StringNode(self, node, context):
        return RTResult().success(
            String(node.tok.value).set_context(context).set_pos(node.pos_start, node.pos_end)
        )

    def visit_ListNode(self, node, context):
        res = RTResult()
        elements = []

        for element_node in node.element_nodes:
            elements.append(res.register(self.visit(element_node, context)))
            if res.error: return res

        return res.success(
            List(elements).set_context(context).set_pos(node.pos_start, node.pos_end)
        )

总结

接下来的话,我们的实现就要到尾声了,当然接下来我们要处理的是这个,怎么和中文联系起来。先前我们一直做到的是中文,但是在后面,发现用中文实在是太啰嗦了,但是这个玩意的目标用户又是小学生,淦。

如果你对Python感兴趣,想要学习python,这里给大家分享一份Python全套学习资料,都是我自己学习时整理的,希望可以帮到你,一起加油!

😝有需要的小伙伴,可以V扫描下方二维码免费领取🆓

1️⃣零基础入门

① 学习路线

对于从来没有接触过Python的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

② 路线对应学习视频

还有很多适合0基础入门的学习视频,有了这些视频,轻轻松松上手Python~
在这里插入图片描述

③练习题

每节视频课后,都有对应的练习题哦,可以检验学习成果哈哈!
在这里插入图片描述

2️⃣国内外Python书籍、文档

① 文档和书籍资料

在这里插入图片描述

3️⃣Python工具包+项目源码合集

①Python工具包

学习Python常用的开发软件都在这里了!每个都有详细的安装教程,保证你可以安装成功哦!
在这里插入图片描述

②Python实战案例

光学理论是没用的,要学会跟着一起敲代码,动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。100+实战案例源码等你来拿!
在这里插入图片描述

③Python小游戏源码

如果觉得上面的实战案例有点枯燥,可以试试自己用Python编写小游戏,让你的学习过程中增添一点趣味!
在这里插入图片描述

4️⃣Python面试题

我们学会了Python之后,有了技能就可以出去找工作啦!下面这些面试题是都来自阿里、腾讯、字节等一线互联网大厂,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
在这里插入图片描述
在这里插入图片描述

上述所有资料 ⚡️ ,朋友们如果有需要的,可以扫描下方👇👇👇二维码免费领取🆓

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

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

相关文章

postman后端测试时invalid token报错+token失效报错解决方案

报错信息1{“msg”:“invalid token”,“code”:401} 没有添加postman的token信息 报错信息2{“msg”: “token失效&#xff0c;请重新登录”,“code”: 401} 写了token但是token信息写的是错的,会提示token失效 解决方案如下 仅写完后端的查询,但是前端还没写的时候,可…

阿里云容器服务助力万兴科技 AIGC 应用加速

作者&#xff1a;子白&#xff08;顾静&#xff09; 2023 年堪称是 AIGC 元年&#xff0c;文生图领域诞生了 Stable Diffusion 项目&#xff0c;文生文领域诞生了 GPT 家族。一时间风起云涌&#xff0c;国内外许多企业投身 AIGC 创新浪潮&#xff0c;各大云厂商紧随其后纷纷推…

Vue3 + Electron框架读取程序外部配置文件

网上找了一堆都不行&#xff0c;根据这个步骤来肯定能用 1. 在项目下新建一个config.json文件 2. json文件中写入一些配置 3. vue.config.js中配置打包时把config.json文件copy到应用目录下 pluginOptions:{electronBuilder:{nodeIntegration:true,builderOptions: {extraReso…

lua使用resty.http做nginx反向代理(https请求,docker容器化部署集群),一个域名多项目转发

下载使用 链接&#xff1a;https://pan.baidu.com/s/1uQ7yCzQsPWsF6xavFTpbZg 提取码&#xff1a;htay –来自百度网盘超级会员V5的分享 在根目录下执行: # 从 github 上下载文件 git clone https://github.com/ledgetech/lua-resty-http.git # 将 lua-resty-http/lib/ 下的 r…

linux-nfc neard 编译、安装与运行

项目github地址&#xff1a; https://github.com/linux-nfc/neard git clone地址&#xff1a; https://github.com/linux-nfc/neard.git 1.安装依赖库 clone完源码切换到目录neard里。这个项目需要依赖一下库&#xff1a; - GCC compiler - D-Bus library - GLib library …

【汇编】实验12 编写0号中断的处理程序

记录一下代码 assume cs:code code segment start:mov ax,csmov ds,axmov si,offset do0mov ax,0mov es,axmov di,200hmov cx,offset do0end-offset do0cldrep movsb ;将ds:si的字节单元byte送入es:di&#xff0c;也就是将从do0处往下的指令复制到0:200h中。mov word ptr es:[…

VS生成C++动态链接库DLL

1、官方文档&#xff1a; 在 Visual Studio 中创建 C/C DLL | Microsoft Learn 演练&#xff1a;创建和使用自己的动态链接库 (C) | Microsoft Learn 创建和调用步骤&#xff0c;主要按照第二篇操作。 一、建立DLL项目生成库文件 直接建立C的DLL项目&#xff0c;注意叙述&am…

小程序商城 免 费 搭 建之java商城 电子商务Spring Cloud+Spring Boot+二次开发+mybatis+MQ+VR全景+b2b2c

java SpringCloud版本b2b2c鸿鹄云商平台全套解决方案 使用技术&#xff1a; Spring CloudSpring BootMybatis微服务服务监控可视化运营 B2B2C平台&#xff1a; 平台管理端(包含自营) 商家平台端(多商户入驻) PC买家端、手机wap/公众号买家端 微服务&#xff08;30个通用…

Datawhale 强化学习笔记(二)马尔可夫过程,DQN 算法

文章目录 参考马尔可夫过程DQN 算法&#xff08;Deep Q-Network&#xff09;如何用神经网络来近似 Q 函数如何用梯度下降的方式更新网络参数强化学习 vs 深度学习 提高训练稳定性的技巧经验回放目标网络 代码实战 DQN 算法进阶Double DQNDueling DQN 算法代码实战 参考 在线阅…

【印象深刻的实战经历】两次全国大学生数学建模经历分享

目录 &#x1f33c;初次接触 初次参加培训 分享培训所得 比赛开始 &#x1f525;再次接触 参加校赛 机缘巧合 再次培训 比赛开始 &#x1f4d5;技巧总结 从问题的实际意义分析大体上可分为 从问题的解决方法上分析 做国赛题目的步骤 赛前准备 选题 寻找思路…

蓝桥杯每日一题----货物摆放

题目 分析 上来一看&#xff0c;三个for循环&#xff0c;从1到n&#xff0c;寻找满足lwhn的个数&#xff0c;但是这样根本跑不出来答案&#xff0c;n太大了&#xff0c;1e15的级别&#xff0c;O&#xff08;n&#xff09;的时间复杂度都不行&#xff0c;更何况是O&#xff08;…

Docker(一)简介和基本概念

一、简介 本章将带领你进入 Docker 的世界。 什么是 Docker&#xff1f; 用它会带来什么样的好处&#xff1f; 好吧&#xff0c;让我们带着问题开始这神奇之旅。 1.什么是 Docker Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目&…

Jenkins的环境部署,(打包、发布、部署、自动化测试)

一、Tomcat环境安装 1.安装JDK&#xff08;Java环境&#xff09; JDK下载地址&#xff1a;Java Downloads | Oracle 安装好后在系统环境变量里配置环境变量&#xff1a; ①添加JAVA_HOME 变量名&#xff1a;JAVA_HOME变量值&#xff1a;C:\Program Files\Java\jdk1.8.0_18…

docker安装运行CloudBeaver并设置默认语言为中文

1、CloudBeaver CloudBeaver 是一个开源的 Web 数据库管理工具&#xff0c;它提供了一个基于浏览器的用户界面&#xff0c;允许用户管理和操作各种类型的数据库。CloudBeaver 支持多种数据库系统&#xff0c;包括但不限于 PostgreSQL、MySQL、SQLite、Oracle、SQL Server 以及…

ClickHouse学习笔记(六):ClickHouse物化视图使用

文章目录 1、ClickHouse 物化视图2、物化视图 vs 普通视图3、物化视图的优缺点4、物化视图的用法4.1、基本语法4.2、准备表结构4.3、准备数据4.4、查询结果 1、ClickHouse 物化视图 ClickHouse 的物化视图是一种查询结果的持久化&#xff0c;它的存在是为了带来查询效率的提升…

银河麒麟服务器操作系统搭建证书服务器并颁发可用于签名的证书步骤说明

银河麒麟服务器操作系统搭建证书服务器并颁发可用于签名的证书的方法与前面Centos7上&#xff08;centos7 使用openssl 配置证书服务器并颁发证书&#xff08;史上最详细版本&#xff09;_centos phpinfo显示的openssl配置项为/etc/pki/tls/openssl.c-CSDN博客&#xff09;是一…

N-140基于springboot,vue协同过滤推荐算法个性化购物商城

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vueelementUI 服务端技术&#xff1a;springbootmybatisredis 本…

Chondrex:Glycosaminoglycans Assay Kit(糖胺聚糖检测试剂盒)

糖胺聚糖&#xff08;glycosaminoglycans&#xff0c;GAGs&#xff09;是一种携带负电荷的多糖链&#xff0c;位于大多数结缔组织和许多不同类型细胞的细胞外基质&#xff08;extracellular matrices, ECM&#xff09;中以及细胞表面上。由重复双糖单位复合构成的糖胺聚糖可分为…

Kali在Vmware无法连接到网络,配置网络及解决办法

一.问题描述&#xff1a; 打开 Kali&#xff0c;无法连接到网络&#xff0c;虚拟机配置正常的。 尝试 ping 百度&#xff0c;出错&#xff1a; ping baidu.com 提示&#xff1a; ping: baidu.com: Temporary failure in name resolution二.解决办法&#xff1a; 1.首先在vmwa…

FaFu--练习复盘--1

1、输出图形及二维数组应用 1.1.输出图形 描述 编写程序打印n行如下图形&#xff0c;其中1≤n≤500。 输入用例 7 输出用例 具体实现 #include"stdio.h" int main(){int n,i,j;scanf("%d",&n);for(i 1; i< n;…