第四讲:编程范式
- 前端的主要编程语言为JavaScript
- JavaScript作为一种融合了多种编程范式的语言,灵活性非常高。
- 前端开发人员需要根据场景在不同编程范式间自如切换
- 进一步需要创造领域特定语言抽象业务问题
课程收益:
-
了解不通过编程范式的起源和适用场景
-
掌握JavaScript在不同的编程范式特别是函数式编程范式的使用
-
掌握创建领域特定语言的相关工具和模式
02编程语言
为什么需要编程语言:用指令告诉机器要干什么
直接使用数字很难输入,因此产生了汇编语言,然后出现了高级语言,高级语言更加贴近于人类的思维。
编译器把高级语言变成汇编,汇编变成低级然后给数据
C功能齐全,可以对地址直接操作,功能齐全————中级语言
C++在c的基础上增加了对类、继承、权限控制等
lisp是函数式语言代表:代码即数据,与机器无关,引入了闭包概念
(闭包是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。 换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。 )
JavaScript:基于圆形和头灯函数等多范式语言
过程式、面向对象、函数式、响应式
03编程范式
什么是编程范式:
程序语言有:是否允许副作用、操作的执行顺序、代码组织、状态管理、语法和词法等程序语言特性,可根据这些对程序语言进行分类
eg:程序编程范式分成命令式(面向过程(操作用过程分组)和面向对象(用对象分组))和声明式(函数式、响应式)两种
过程式:自顶向下,结构化编程
涉及程序要从顶向下设计程序
程序——模块——变量——数据结构
|——函数——函数
|——语句
JS中的面向过程:
用export可以导出数据
面向过程式编程缺点:数据与算法关联弱,不利于修改和火虫,不利于代码重用
面向对象编程:封装、继承、多态、依赖注入。限制了数据的访问方式
面向对象编程——封装
关联数据与算法:
数据与算法分装到一个类里面
继承是可以让某一个类型的对象获得另一个对象的方法
多态:不同的结构可以进行接口共享,进而达到函数复用
面向对象编程——五大原则
单一指责原则SRP
开放封闭原则:在更改性方面是封闭的
历史替换原则:子类可以替换弗雷
依赖倒置原则:
高层模块不应该依赖于底层模块,两者都应该依赖于抽象;抽象不应该依赖于具体实现,具体实现应该依赖于抽象。这个原则的目的是降低代码的耦合性,提高代码的灵活性和可扩展性。通过使用接口和抽象类等技术,使得代码能够适应不同的需求和变化
接口分离原则:
函数式编程:函数是第一等
纯函数五副作用
高阶函数闭包
函数编程可以手动组合也可以进行自动化组合(更方便)
函数式编程:可以当走容器的类型,类型支持对容器内元素进行操作
在容器里的数值进行判断
容器可以嵌套
在容器内进行操作
响应式编程:合并、过滤、转化、异常处理、多播
响应式编程是一种面向数据流和变化传播的编程范式。 这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。 例如,对于a=b+c 这个表达式的处理,在命令式编程中,会先计算b+c 的结果,再把此结果赋值给变量a,因此b,c 两值的变化不会对变量a 产生影响。
对数据流进行转化:最终流出管道的就是纯粹的坐标数据
去除嵌套的Observable
04领域特定语言
DSL:应用于特定领域的语言
与之相对的是通用语言
语言运行:语言字符串经过词法解析器生成短语再通过形成语法树然后就有便利操作,对数进行执行
lexer:词法分析(lexical analysis)是计算机科学中将字符序列转换为单词(Token)序列的过程。进行词法分析的程序或者函数叫作词法分析器(Lexical analyzer,简称Lexer),也叫扫描器(Scanner)。词法分析器一般以函数的形式存在,供语法分析器调用。
主要特点不依靠语法,而只依靠词法原 理已知文法利用递归向下分析有关术语语法分析器、有限状态自动机领 域编译
#include<stdio.h>
#include<string.h>
#include<iostream.h>
char prog[80],token[8];
char ch;
int syn,p,m=0,n,row,sum=0;
char *rwtab[6]={"begin","if","then","while","do","end"};
void scaner()
{
/*
共分为三大块,分别是标示符、数字、符号,对应下面的 if else if 和 else
*/
for(n=0;n<8;n++) token[n]=NULL;
ch=prog[p++];
while(ch==' ')
{
ch=prog[p];
p++;
}
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')) //可能是标示符或者变量名
{
m=0;
while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
{
token[m++]=ch;
ch=prog[p++];
}
token[m++]='\0';
p--;
syn=10;
for(n=0;n<6;n++) //将识别出来的字符和已定义的标示符作比较,
if(strcmp(token,rwtab[n])==0)
{
syn=n+1;
break;
}
}
else if((ch>='0'&&ch<='9')) //数字
{
{
sum=0;
while((ch>='0'&&ch<='9'))
{
sum=sum*10+ch-'0';
ch=prog[p++];
}
}
p--;
syn=11;
if(sum>32767)
syn=-1;
}
else switch(ch) //其他字符
{
case'<':m=0;token[m++]=ch;
ch=prog[p++];
if(ch=='>')
{
syn=21;
token[m++]=ch;
}
else if(ch=='=')
{
syn=22;
token[m++]=ch;
}
else
{
syn=23;
p--;
}
break;
case'>':m=0;token[m++]=ch;
ch=prog[p++];
if(ch=='=')
{
syn=24;
token[m++]=ch;
}
else
{
syn=20;
p--;
}
break;
case':':m=0;token[m++]=ch;
ch=prog[p++];
if(ch=='=')
{
syn=18;
token[m++]=ch;
}
else
{
syn=17;
p--;
}
break;
case'*':syn=13;token[0]=ch;break;
case'/':syn=14;token[0]=ch;break;
case'+':syn=15;token[0]=ch;break;
case'-':syn=16;token[0]=ch;break;
case'=':syn=25;token[0]=ch;break;
case';':syn=26;token[0]=ch;break;
case'(':syn=27;token[0]=ch;break;
case')':syn=28;token[0]=ch;break;
case'#':syn=0;token[0]=ch;break;
case'\n':syn=-2;break;
default: syn=-1;break;
}
}
int main()
{
p=0;
row=1;
cout<<"Please input string:"<<endl;
do
{
cin.get(ch);
prog[p++]=ch;
}
while(ch!='#');
p=0;
do
{
scaner();
switch(syn)
{
case 11: cout<<"("<<syn<<","<<sum<<")"<<endl; break;
case -1: cout<<"Error in row "<<row<<"!"<<endl; break;
case -2: row=row++;break;
default: cout<<"("<<syn<<","<<token<<")"<<endl;break;
}
}
while (syn!=0);
}
parser(语法分析程序)从左到右检查,从做到右构建语法树