首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10000篇文章
优秀的个人博客,低调大师

Docker入门(一)————五叶草

作者: 麦冬 锻石 基础部分 安装 看Docker操作手册写的很详细 二进制安装 一般安装方法 这里说下二进制安装,按照官网方式Install static binaries 安装docker在Centos基本上就是yum install即可的一件事情(在ubuntu也是apt-get很容易安装),但是在实际环境中,可能遇到企业内网之内无法连接,或者安全合规审查等问题,加之下载增加了安装时间,因此二进制方式安装变成了一种实际的需要。下面是官网的安装方式 有些时候会在sudo docker &时候出现错误可以执行以下下面命令再试。 rm -rf /var/lib/docker 脚本傻瓜式安装 这里提供写好的脚本,可以把脚本和下载的二进制文件放在同一目录下,然后运行sh install-docker.sh 项目 下载地址 二进制方式安装docker脚本 http://osgp88fat.bkt.clouddn.com/docker/install-docker.sh 二进制的docker安装文件 https://download.docker.com/linux/static/stable/x86_64/ 程序是否正常 sudo docker info 运行容器 docker run命令提供了Docker容器的创建到启动的功能 如果要在命令行下创建一个我们能与之进行交互的容器,而不是一个运行后台服务的容器,则-i -t这两个参数是必不可少的基本参数。 apt-get update && apt-get install vim //在容器中安装vim exit // 推出容器,返回到Ubuntu容器的宿主机命令行提示符中 查看当前系统中容器列表 sudo docker ps -a 加上-a 会显示正在运行的 和已经停止的所有容器 容器命名 一般docker会为创建的容器生成随机名称,生成指定名称用--name sudo docker run --name jxtreehouse -i -t ubuntu /bin/bash 附着到容器上 像上面那样,docker容器重新启动时候,会沿用docker run 命令的参数来运行,因此我们容器重新启动后会运行一个交互式会话shell。当然,我们也可以用docker attach命令 重新附着到该容器的会话上 sudo docker attach jxtreehouse 创建守护式容器 除了交互式运行的容器,我们也可以创建长期运行的容器:守护式容器特点: 没有交互会话 非常适合运行应用程序和服务所以,大多数时候我们都需要守护式运行我们的容器 sudo docker run --name jxbooks -d ubuntu /bin/sh -c "while true;do echo hello jx;sleep 1; done" 从上面运行结果我们可以看出,docker run命令并没有像上一个容器那样将主机的控制台附着到新的shell会话上,仅仅返回一个容器ID而已,我们还是在主机的命令中。 容器内部运行情况 ### 查看日志 sudo docker jxbooks sudo docker logs jxbooks // 获取守护式容器日志 sudo docker logs -f jxbooks // 跟踪守护式容器的日志(-f 类似 tail -f) sudo docker logs -ft jxbooks // 跟踪守护式容器的最新日志 (加上时间戳) 查看容器内的进程 sudo docker top jxbooks 在容器内部运行进程 在Docker3.1之后,我们可以通过docker exec命令在容器内部额外启动新程序。可以在容器内运行的进程有两种: 后台任务 交互式任务 后台任务例子: sudo docker exec -d jxbooks touch /etc/new_config_file 在jxbooks容器中启动一个如打开shell的交互任务: sudo docker exec -t -i jxbooks /bin/bash 和交互式容器一样,这里-t -i 标志为我们执行的进程创建TTY并捕捉STDIN。 停止守护式容器 sudo docker stop jxbooks jxbooks也可用容器的ID来替代(查看id 可用 docker ps -a) 自动重启容器 sudo docker run --restart=always --name jxbooks -d ubuntu /bin/sh -c "while true; do echo hello world; sleep1;done" --restart=always 无论容器的退出代码是什么,docker都会自动重启容器还可以设置成 on-failure (只有当容器退出代码为非 0 时候 才会自动重启) --restart=on-failture:5 //重启次数参数 深入容器 sudo docker inspect jxbooks 删除容器 sudo docker rm 容器名或id //注意运行中的docker容器无法删除,必须先docker stop 或者 docker kill 停止容器 删除所有容器 docker rm `docker ps -a -q` 导入docker镜像并开发 在合作开发时候,我们拿到一个项目打包好的docker镜像(原开发环境为ubuntu,我们这里拿到镜像后 还用ubuntu做演示,实际开发时候,你可能是在windows 或者centos中拿到这个项目镜像来开发) 比如压缩包名datascreen.tar 我们在根目录/data文件夹中建一个docker文件夹,进入文件夹,新建tmp文件夹 ,在docker同级目录下新建 datascreen 文件夹 我们将把项目docker镜像文件映射到这里 导入容器 sudo docker load < ${PATH}/datascreen.tar //加载docker打包的镜像 到本宿主机 运行Docker sudo docker images //获取image ID sudo docker run -d -it -p 7001:7001 -v /data/docker/tmp/:/tmp/ {image id} /bin/bash /data/docker/tmp/ 映射到的宿主机上的目录/tmp/ {image id} /bin/bash 镜像中的目录 启动容器 sudo docker ps //查看运行中的容器 sudo docker start f1737f42f13b //f1737f42f13b 是容器id sudo docker exec -it f1737f42f13b /bin/bash 运行data-screen cd data-screen/ nohup ./auto-run.sh & auto-run.sh脚本中的内容,主要是启动mongod 和用node启动入口文件index.js (这个项目是一个nodejs的后台项目) 停止docker 容器 (!危险) 如果是运行在应用上的程序和服务的守护式容器 注意不要随意停止 sudo docker ps sudo docker stop <CONTAINER ID> 参考 Docker操作手册 第一本docker书

优秀的个人博客,低调大师

普通程序员如何入门AI

毫无疑问,人工智能是目前整个互联网领域最火的行业,随着AlphaGo战胜世界围棋冠军,以及各种无人驾驶、智能家居项目的布道,人们已经意识到了AI就是下一个风口。当然,程序员是我见过对于新技术最敏感的一个人群,举一个例子:当TensorFlow刚刚面世的时候,几乎所有搞大数据的同学一见面就开始交流这方面的内容,仿佛所有人一夜之间成了“TFboys”(tensorflow_boys)。我觉得之所以程序员对于新技术很敏感有两个原因,其一是技术这碗饭会逼着你不停地去学习,不然很快会被淘汰;其二是程序员大多是理工男,对于新事物是充满好奇的。借着出版《机器学习实践应用》的机会 ,又是到了这样一个档口,就是很多程序员面临转型,需要去学习AI技术,在这一文我就简单谈谈我对学习AI的一些看法,我自己的肤浅想法,轻喷哈。 (先声明下我不是算法大神,我

优秀的个人博客,低调大师

《SAP入门经典(第5版)》——导读

前言 我们已经介绍了SAP的基本情况,以及“SAP领域和我们的生活在过去几年已经发生了巨变,我很高兴与SAP的新手和老兵们分享这一变化。”这是2011年本书第4版前言中George Anderson所说的话。当他和Sams邀请我作为这一最新版本的主要作者时,这句话也反映了我的心情:这一任务令我兴奋!也是我的荣幸。我是认真的!实际上,George和我分担了重写和编辑这一版本的工作。我们引入了更多的屏幕截图和其他插图,修订了格式,同时保留了旧版本中最有教益的方面。 此外,我们增加了大量新内容。从HANA内存数据库和托管平台等新技术的介绍,到SAP的新用户界面、新推出的“软件即服务”云解决方案、新报表应用等,我们实际上重新编写了许多章节。 因为IT领域的变化通常很迅速,我们发现提供比以往更广泛的基础是很有益的。我们加入了物联网、新型移动设备技术和社会化媒体及大数据对IT领域的改变等新主题,还简短介绍了数据安全性威胁和其他发展,以及合理或者可能的未来趋势。我们这样做的目标是帮助您更深入地思考SAP的适用场合、不足之处,以及未来最有可能面临挑战的方面。 感谢您选择本书最新、最好的版本。我们相信,您将会发现值得花费时间阅读它。本书的各章组织成5个容易使用的部分。第1部分自然地从所有基础知识的介绍开始。第2部分涵盖了SAP的新旧业务应用及组件。 这样,我们就为从业务用户的角度(第3部分)和IT专业人员的角度(第4部分)探索SAP打好了基础。第5部分用3章进行总结,帮助您开始或者提升SAP职业生涯。 在学习过程中,我们介绍了对SAP新人最重要的知识。对于业务用户,我们组合了几个章节,简单介绍了实际的商业事务。我们探索了SAP在创建销售订单、检查客户记录、更新员工记录等方面的作用。我们提供了SAP Business Suite中用于执行常见商业事务的事务代码,而且不仅从SAP ERP本身、还从SAP Business Objects和其他应用中探索了所执行的报表及查询过程。这样,未来的SAP业务用户将对许多SAP最终用户生命中的一天有更好的感性认识。 对于技术用户,我们提供更深入的内容,并且已经完成了一些特别有益的工作。读者的反馈让我们得知,在SAP Service Marketplace、Developer Network、Help Portal和各种博客中寻找基本安装指南、重要技术信息等很困难。因此我们在技术细节说明的旁边加入了详细的“如何查找”材料。 我们还简单地介绍了SAP试用版本的安装,包括场内安装和云安装。有了“真正”的SAP系统,您就能更好地实时应用我们在这24章中一起探索的知识。我们还探索了SAP开发人员的世界,观察技术升级的准备,研究SAP实施项目管理的必要步骤。通过从多个不同视角介绍SAP技术,包括与SAP及云计算相关的最新透视,即使较有经验的技术读者也能在工作中感受到影响。 掌握了新的观点和知识,我们的读者将比以往更加高效。您将成为稀有的人才,有足够广博的知识理解大局,有足够的才能认识到面前仍有漫漫长路。但是仅依靠本书的知识,就能够很好地改变自己、您的职业生涯和未来。前言 第1部分 SAP简介 [第1章 SAP是什么 1.1 SAP软件公司简介](https://yq.aliyun.com/articles/103980)1.2 SAP业务应用程序1.3 SAP集团概念1.4 小结1.5 案例分析[第2章 SAP业务基础知识 2.1 业务架构和业务线路图](https://yq.aliyun.com/articles/103995)2.2 SAP的目的:运行业务2.3 其他视角:将业务需求映射到SAP应用程序中2.4 SAP业务流程样例2.5 小结2.6 案例分析第3章 SAP技术基础知识第4章 SAP项目基础知识第2部分 SAP应用程序与组件第5章 SAP应用程序与组件概述第6章 SAP NetWeaver与HANA第7章 SAP ERP和业务套件第8章 云上的SAP和新SAP解决方案第3部分 业务用户SAP基础知识第9章 从业务用户的视角看SAP的使用第10章 SAP传统及新用户界面的使用第11章 使用SAP ERP完成工作第12章 其他SAP业务套件应用的使用第13章 使用SAP输出报表第14章 简化财务和办公室集成的使用第4部分 IT专家所需的SAP知识第15章 SAP项目经理的视角第16章 从技术专家的视角看SAP第17章 SAP开发人员的视角第18章 SAP安装与实施第19章 SAP与云第20章 SAP系统管理第21章 SAP增强、升级和其他补强措施第5部分 SAP职业生涯第22章 业务用户的SAP职业生涯第23章 IT专业人员的SAP职业生涯第24章 其他资源和结语附录A 案例分析答案

优秀的个人博客,低调大师

《Swift入门经典(第2版)》——导读

**前言**在Apple公司于2014年6月召开的年度全球开发者大会(World Wide Developer Conference,WWDC)上,Apple公司公布了一种名为Swift的新编程语言,该公司从2010年起就在开发这种语言。这是一个重大的公告。多年来,在开发大多数Mac和iOS应用时,人们主要选择的语言是Objective-C。可以明显感觉到人们对Swift编程语言的殷切期盼。Twitter上关于Swift的言论不绝于耳,人们纷纷购买标题中具有Swift的域名,并且在公告后24小时内,Apple公司Swift iBook的下载量超过30万次。人们为这种改变做好了准备。 但是,一种新语言不仅会带来语法上的区别,还会带来习惯的差异和新的约定。Swift不仅是一种面向对象语言,它还引入了从其他语言收集到的特性,比如C#、Haskell、Ruby等。Swift被标榜为“没有C的Objective-C”,它在过去一年经历了如此大的改进,以至于有时很难看出它们的任何相似之处。Swift构建于Objective-C中大家熟知的概念之上,但是它还包括了更现代、更安全的语法和多种范式(paradigm),比如,面向对象、函数式、强制性和块结构化,以及在WWDC 2015上把自身重新定义为一种面向协议的编程语言。 官方现在公布的Swift版本是2.0,但它仍然在演进,甚至在编写本书时,还有更多的改变融入了Beta版。话虽如此,本书目前还是针对Swift 2.0和Xcode 7。如果你在这些示例中发现了与书中描述的内容或者与界面不一致的地方,请检查Apple的发布文档和本书的电子版本,因为它们可能比你手上纸质的图书更新起来要快得多。 目录 **[第1章 Swift开发环境简介1.1 什么是Swift](https://yq.aliyun.com/articles/98471)**1.2 起步1.2.1 四处看看1.2.2 Xcode playground1.2.3 Swift REPL1.3 小结1.4 问与答1.5 作业1.5.1 测验1.5.2 答案1.5.3 练习**[第2章 学习Swift的基本数据类型2.1 Swift中的常量](https://yq.aliyun.com/articles/98492)**2.2 Swift中的变量2.3 数据类型简介2.3.1 类型推断2.3.2 数据类型2.3.3 初始化值2.4 小结2.5 问与答2.6 作业2.6.1 测验2.6.2 答案2.6.3 练习

优秀的个人博客,低调大师

[非常短]iOS开发入门教程

如果你想学习iOS开发并且和我一样对Mac及iOS一无所知的话,这个系列教程也许适合你。http://www.raywenderlich.com/tutorials, 有些甚至有中文,虽然翻译的不太好。 作者: 峻祁连 邮箱:junqilian@163.com 出处: http://junqilian.cnblogs.com 转载请保留此信息。 本文转自峻祁连. Moving to Cloud/Mobile博客园博客,原文链接:http://www.cnblogs.com/junqilian/archive/2012/10/05/2712011.html ,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android 黑客入门 Http与Https协议

上篇文章中提及了Android的JS脚本漏洞问题,调用addJavascriptInterface的编译必须为API 17及以上,为了兼容更多的设备,App和第三方框架程序经常用低的API版本编译,所以即使运行在打了补丁程序的Android 4.2, 4.3或4.4的设备上,App仍存在漏洞攻击风险。 代码的执行意味着对设备的无限制访问, 以futex漏洞为例(CVE-2014-3153),它影响当前使用的每个Linux内核版本,包括安卓系统以及最近第一次被成功 root的Galaxy S5 。尽管他们不是等价的,但我们还是应该养成“远程代码执行”与“root权限”在严重等级上等价的习惯,因为迟早,一个下定决心的黑客将可能从一个地方蹦到另一个地方,获取设备的完全控制权。 我们来验证一下漏洞到底有多容易被利用。 我们来测试 通过中间代理 攻击劫持 非安全的javascript下载,并注入一些javascript脚本来探查addJavascriptInterface漏洞。 我设置了一个充当透明Web Proxy 的wifi无线接入点(LP)。设置了对任何接入此设备通过HTTP请求任何脚本时都注入恶意代码。 即使当LP不受控制时,DNS毒化或ARP缓存欺骗等技术也可以用来实现中间人代理。或者可以安装一个模仿成合法LP的假LP。也就是说,可以有各种方法实现中间人的代理,使用wifi的任何人都将通过我们的代理访问网络,为什么最好不要连接到非安全的wifi,这也是其中之一的原因。 javascript的脚本是具有动态性的,意味着我们不需要检测特定的程序或框架程序以作为目标。当运行时,恶意代码扫描整个javascript命名空间中的对象,查找不正确地使用了addJavascriptInterface API的对象,然后对每个进行漏洞测试。如果没有找到漏洞,它就退出。如果成功了,它将运行一个shell命令启动某个app。 function findVulnerableObject() { for (var prop in window) { try { // If getClass() doesn’t throw, the object is vulnerable window[prop].getClass(); return window[prop]; }catch(e) { ……} return null; } 后来成功的运行了,如果动态更改js代码,也是可以运行的,通过查看TCP/IP包和监控轨迹,很快发现很多框架程序只是联合使用了addJavascriptInterface和非安全HTTP下载的罪魁祸首。 结论——可以看出,通过使用相对简单的中间代理人技术,无需特定的应用程序、设备、场景、就可以远程运行一些脚本程序。 因此,建议开发者,当连接到一个不可信的wi-fi无线网络时不要使用任何Android应用程序来显示广告,建议用户,尽量不要连接不可信的Wifi,没有密码则更为危险,随时会被抓包、攻破、篡改,分析信息。 在调查的框架程序中只有少数使用Https,也就意味着任何使用这些框架程序的app在非安全地下载javascript时也易受到攻击。去年的调查数据还是有将近40%的程序在使用http,不过一些海外市场已经强制要求使用Https,苹果也不例外。 现在新的6.0 和 7.0 牛轧糖 安卓版本中,权限代理回到了程序申请,这意味着未来的app将更为安全,如果用户不同意,则无法处理,而且无法一次性永久申请权限,加之Root权限的限制,其实以后会越来越安全了。

优秀的个人博客,低调大师

word2vec 入门基础(一)

一、基本概念 word2vec是Google在2013年开源的一个工具,核心思想是将词表征映 射为对应的实数向量。 目前采用的模型有一下两种 CBOW(Continuous Bag-Of-Words,即连续的词袋模型) Skip-Gram 项目链接:https://code.google.com/archive/p/word2vec 二、背景知识 词向量 词向量就是用来将语言中的词进行数学化的一种方式,顾名思义,词向量 就是把一个词表示成一个向量。这样做的初衷就是机器只认识0 1 符号,换句话说,在自然语言处理中,要想让机器识别语言,就需要将自然语言抽象表示成可被机器理解的方式。所以,词向量是自然语言到机器语言的转换。 词向量有一下两种 One-hot Representation 向量的长度为词典的大小,向量的分量只有一个 1,其他全为 0, 1 的位置对应该词在词典中的位置,例如 “话筒”表示为 [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ...] “麦克”表示为 [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ...] 优点: 如果使用稀疏方式存储,非常简洁,实现时就可以用0,1,2,3,...来表示词语进行计算,这样“话筒”就为3,“麦克”为8. 缺点:1.容易受维数灾难的困扰,尤其是将其用于 Deep Learning 的一些算法时;2.任何两个词都是孤立的,存在语义鸿沟词(任意两个词之间都是孤立的,不能体现词和词之间的关系)。 Distributional Representation 最早是 Hinton 于 1986 年提出的,可以克服 one-hot representation的缺点。解决“词汇鸿沟”问题,可以通过计算向量之间的距离(欧式距离、余弦距离等)来体现词与词的相似性 。 其基本想法是直接用一个普通的向量表示一个词,这种向量一般长成这个样子:[0.792, −0.177, −0.107, 0.109, −0.542, ...],常见维度50或100。 优点:解决“词汇鸿沟”问题 缺点:训练有难度。没有直接的模型可训练得到。所以采用通过训练语言模型的同时,得到词向量 。 当然一个词怎么表示成这么样的一个向量是要经过一番训练的,训练方法较多,word2vec是其中一种。值得注意的是,每个词在不同的语料库和不同的训练方法下,得到的词向量可能是不一样的。 词向量在机器翻译领域的一个应用,就是google的TomasMikolov 团队开发了一种词典和术语表的自动生成技术,该技术通过向量空间,把一种语言转变成另一种语言,实验中对英语和西班牙语间的翻译准确率高达90%。 论文http://arxiv.org/pdf/1309.4168.pdf在介绍算法工作原理的时候举了一个例子:考虑英语和西班牙语两种语言,通过训练分别得到它们对应的词向量空间 E 和 S。从英语中取出五个词 one,two,three,four,five,设其在 E 中对应的词向量分别为 v1,v2,v3,v4,v5,为方便作图,利用主成分分析(PCA)降维,得到相应的二维向量 u1,u2,u3,u4,u5,在二维平面上将这五个点描出来,如下图左图所示。类似地,在西班牙语中取出(与 one,two,three,four,five 对应的) uno,dos,tres,cuatro,cinco,设其在 S 中对应的词向量分别为 s1,s2,s3,s4,s5,用 PCA 降维后的二维向量分别为 t1,t2,t3,t4,t5,将它们在二维平面上描出来(可能还需作适当的旋转),如下图右图所示: 观察左、右两幅图,容易发现:五个词在两个向量空间中的相对位置差不多,这说明两种不同语言对应向量空间的结构之间具有相似性,从而进一步说明了在词向量空间中利用距离刻画词之间相似性的合理性。 语言模型 语言模型其实就是看一句话是不是正常人说出来的。意义在于机器翻译、语音识别得到若干候选之后,可以利用语言模型挑一个尽量靠谱的结果。在 NLP 的其它任务里也都能用到。 用数学符号描述为:给定一个字符串"w1,w2,...,wt",计算它是自然语言的概率P(w1,w2,…,wt)。w1到wT 依次表示这句话中的各个词。有个很简单的推论是: p(s)=p(w1,w2,⋯wT)=p(w1)p(w2|w1)p(w3|w1,w2)⋯p(wt|w1,w2,⋯wT−1) 上面概率公式的意义为:第一次词确定后,看后面的词在前面次出现的情况下出现的概率。 例如,有个句子“大家喜欢吃苹果”,一共四个词"大家,喜欢,吃,苹果" P(大家,喜欢,吃,苹果)=p(大家)p(喜欢|大家)p(吃|大家,喜欢)p(苹果|大家,喜欢,吃) p(大家)表示“大家”这个词在语料库里面出现的概率; p(喜欢|大家)表示“喜欢”这个词出现在“大家”后面的概率; p(吃|大家,喜欢)表示“吃”这个词出现在“大家喜欢”后面的概率; p(苹果|大家,喜欢,吃)表示“苹果”这个词出现在“大家喜欢吃”后面的概率。 把这些概率连乘起来,得到的就是这句话平时出现的概率。 如果这个概率特别低,说明这句话不常出现,那么就不算是一句自然语言,因为在语料库里面很少出现。如果出现的概率高,就说明是一句自然语言 为了表示简单,上面的公式用下面的方式表示 p(s)=p(w1,w2,⋯wT)=∏i=1Tp(wi|Contexti) 其中,如果Contexti是空的话,就是它自己p(w),另外如“吃”的Context就是“大家”、“喜欢”,其余的对号入座。 现有模型有 N-gram模型 N-pos模型 ... Bengio的NNLM C&W 的 SENNA M&H 的 HLBL Mikolov 的 RNNLM Huang 的语义强化 N-gram 模型 接下来说 p(wi|Contexti) 的计算方法,上面看的是跟据这句话前面的所有词来计算,这样计算就很复杂,像上面那个例子得扫描四次语料库,这样一句话有多少个词就得扫描多少趟。语料库一般都比较大,越大的语料库越能提供准确的判断。这样计算开销太大。 可以想到的优化方法就是提前将p(wi|Contexti) 提前算好了,那么根据排列组上面的来算,对于一个只有四个词的语料库,总共就有4!+3!+2!+1!个情况要计算,那就是24个情况要计算;换成1000个词的语料库,就是∑i=11000i! 个情况需要统计,对于计算机来说,这根本不可能。 所以诞生了N-gram模型是大词汇连续语音识别中常用的一种语言模型,对中文而言,我们称之为汉语语言模型(CLM, Chinese Language Model)。汉语语言模型利用上下文中相邻词间的搭配信息,在需要把连续无空格的拼音、笔画,或代表字母或笔画的数字,转换成汉字串(即句子)时,可以计算出最大概率的句子,从而实现从到汉字的自动转换,无需用户手动选择,避开了许多汉字对应一个相同的拼音(或笔画串、数字串)的重码问题。 我们给定一个词,然后猜测下一个词是什么。当我说“艳照门”这个词时,你想到的下一个词时什么?我想大家很有可能会想到“陈冠希”,基本上不会有人想到“陈志杰”吧。N-Gram模型的主要思想就是这样的。 上面的context都是这句话中这个词前面的所有词作为条件的概率,N-gram就是只管这个词前面的n-1个词,加上它自己,总共n个词,计算p(wi|Contexti) 只考虑用这n个词来算,换成数学的公式来表示,就是 p(wi|Contexti)=p(wi|wi−n+1,wi−n+2,⋯,wi−1) 一般来说,n取2或者3 假设词表中词的个数 |V| = 20,000 词,那么有下面的一些数据。 照图中的数据看去,取n=3是目前计算能力的上限了。在实践中用的最多的就是bigram和trigram了,而且效果也基本够了。 如果一个词的出现仅依赖于它前面出现的一个词,那么我们就称之为bigram。即 P(T) = P(W1W2W3…Wn)=P(W1)P(W2|W1)P(W3|W1W2)…P(Wn|W1W2…Wn-1)≈P(W1)P(W2|W1)P(W3|W2)…P(Wn|Wn-1) 如果一个词的出现仅依赖于它前面出现的两个词,那么我们就称之为trigram。 那么我们怎么得到P(Wn|W1W2…Wn-1)呢?一种简单的估计方法就是最大似然估计(Maximum Likelihood Estimate)了。即P(Wn|W1W2…Wn-1) = (C(W1 W2…Wn))/(C(W1 W2…Wn-1)) 下面我们用bigram举个例子。假设语料库总词数为13,748 P(I want to eat Chinese food) =P(I)P(want|I)P(to|want)P(eat|to)P(Chinese|eat)*P(food|Chinese) =(3437/13748)*(1087/3437)*(786/1215)*(860/3256)*(19/938)*(120/213) =0.000154171 N-gram模型也会有写问题,总结如下: 1、n不能取太大,取大了语料库经常不足,所以基本是用降级的方法 2、无法建模出词之间的相似度,就是有两个词经常出现在同一个context后面,但是模型是没法体现这个相似性的。 3、有些n元组(n个词的组合,跟顺序有关的)在语料库里面没有出现过,对应出来的条件概率就是0,这样一整句话的概率都是0了,这是不对的,解决的方法主要是两种:平滑法(基本上是分子分母都加一个数)和回退法(利用n-1的元组的概率去代替n元组的概率)

优秀的个人博客,低调大师

带你走进Spark编程之Scala入门

写在前边的话: 1:什么是Scala? Scala是一门多范式的编程语言,类似于Java,并集成了面向对象编程和函数式编程的各种特性,具体可参考知乎上的一个帖子 2:本篇博客包含哪些内容? Scala中变量的声明与函数定义 Scala中的控制结构 Scala中的数据类型 Scala中的类详解 1:变量声明与函数定义 变量声明:val 和 var ,两者的区别是val声明的变量是不可变的,而var声明的变量可变 eg: scala> val a = 12; a: Int = 12 scala> a = 13 <console>:12: error: reassignment to val a = 13 ^ scala> var b =11 b: Int = 11 scala> b = 12 b: Int = 12 函数定义: 带返回值: scala> def max(x:Int,y:Int):Int = { | if(x>y) x | else y | } max: (x: Int, y: Int)Int scala> max(1,2) res5: Int = 2 不带返回值: scala> def helloworld()=println("HelloWorld") helloworld: ()Unit 2:控制结构 1) 判断(if) 类似于上边函数定义中的max函数 2) 循环(while/do) while 语句包括状态判断和循环体,只要当前状态判断为真,就执行循环体一遍,然后进行下一状态判断,判断为假时终止判断,形如 while (A)B do语句和while相反,先执行循环体一遍,然后进行状态判断,状态判断为真,则继续执行循环体,否则终止循环,形如 do B while(A) scala> var m =3 m: Int = 3 scala> while(m!=0){ | println(m) | m -= 1 | } 3 2 1 scala> var n =3 n: Int = 3 scala> do{ | println(n) | n -= 1 | }while(n!=0) 3 2 1 3) 枚举(for) for的两种实现 scala> for(i<- 1 to 3) | println(i) 1 2 3 scala> for(i<- 1 until 3) | println(i) 1 2 4) 匹配(match表达式) scala中的match类似于其他语言中的switch,从上往下进行匹配 scala> val a = "gyt" a: String = gyt scala> a match{ | case "gyt" => println("OK") | case "cyan" => println("no") | } OK 5) 异常处理(throw/try) Scala通过throw抛出一个异常,其异常捕获和处理与java十分类似 scala> if(true){ | println("throw new exception") | throw new IllegalArgumentException | } throw new exception java.lang.IllegalArgumentException ... 35 elided scala> try{ | val file = new FileReader("input.txt") | }catch{ | case ex: FileNotFoundException => //handle missing file | case ex: IOException => //handle other I/O error | }finally{ | println("end") | } end 6) 输出(print/println) 一个不换行输出,一个换行输出 7) 输入(readline) 输入通常使用read函数,readline是从控制台输如一行,指定类型为readT,T为类型,例如readInt scala> val name = readLine("Your Name: \n") warning: there was one deprecation warning; re-run with -deprecation for details Your Name: name: String = thinkgamer 8) 其他语句 return语言表示返回某个值,但是Scala事实上无需使用return语句,对于函数来说,其默认返回值是最后出现的一个值,不用特别注明,如需要返回的值,并非最后出现时,可在函数体后加上该值的标识符使之出现,声明函数时使用return语句,必须声明返回值类型,例如 def max:T = {return} break/continue在C++中非常常见的控制结构语句,但在Scala中是不必要的,可以使用布尔值类型的两通过if语句进行控制 3:数据结构 1) 数组 使用new来实例化一个类,当你创建一个对象的实例时,你可以使用数值或者类型参数 scala> val abc = new Array[String](3) abc: Array[String] = Array(null, null, null) scala> abc(0)="thinkgamer" scala> abc(1)="cyan" scala> abc(2)="GQ" scala> for(i<- 0 to 2) | println(abc(i)) thinkgamer cyan GQ 这里也说明下为什么Scala使用()来访问数组元素,在Scala中,数组和其他普遍的类的定义一样,没有什么特别之处,当你某个值后面使用()时,Scala将其翻译成对应对象的apply方法,因此本例中abc(0)其实调用abc.apply(0)方法,这种表达方法不仅仅只限于数据,对于任何对象,如果在其后面使用(),都将调用该对象的apply方法,同一,日过对某个使用()的对象复制,比如 abc(0)="thinkgamer" scala将这种复制转换为该对象的update方法,也就是abc.update(0,"thinkgamer"),因此上边的例子也可以使用传统的方法调用,可以写成: scala> val new_abc = new Array[String](3) new_abc: Array[String] = Array(null, null, null) scala> new_abc.update(0,"aaa") scala> new_abc.update(1,"bbb") scala> new_abc.update(2,"ccc") scala> for(i<- 0 to 2) | println(new_abc(i)) aaa bbb ccc 从这点来收,数组在scala中并不是某种特殊的数据类型,和普通的类并没有什么区别 不过scala还是提供了初始化数组的简单的方法,上述的例子可以这样写: scala> val abc = Array("thinkgamer","cyan","GQ") abc: Array[String] = Array(thinkgamer, cyan, GQ) 数组的输出可以采用while或者foreach或者for进行输出 scala> val abc = Array("thinkgamer","cyan","GQ") abc: Array[String] = Array(thinkgamer, cyan, GQ) scala> var i = 0 i: Int = 0 scala> while(i<abc.length){ | println(abc(i)) | i+=1 | } thinkgamer cyan GQ scala> abc.foreach(str=>println(str)) thinkgamer cyan GQ 2):Lists Scala的List和Java不同,不能被改变,这样做的一个好处是方法与方法之间关联性较小,从而方法变得更可靠和重用性高,使用这个规则也就意味着变量的设置是不可修改的,这也就避免了多线程访问的互锁问题 scala> val one = List(1,2,3) one: List[Int] = List(1, 2, 3) scala> val two = List(4,5) two: List[Int] = List(4, 5) scala> val three = one:::two three: List[Int] = List(1, 2, 3, 4, 5) :::方法表示连接两个列表,当然列表定义了::方法(右操作符),用于向列表添加元素 scala> val four = three::6::7::Nil four: List[Any] = List(List(1, 2, 3, 4, 5), 6, 7) scala> val five = 1::2::3::4::Nil five: List[Int] = List(1, 2, 3, 4) Nil表示空列表 Scala的List类还定义了其他很多很有用的方法,比如head,last,length,reverse,tail等这里就不一一说明了,具体可以参考List的文档 3) Tuples Scala中另外一个很有用的容器类是Tupels,和Lists不同的Tuples可以包含不同类型的数据,而List只能包含同类型的数据,Tuples在方法需要返回多个结果时非常有用(Tuple对应数学的矢量的概念) 一旦定义了一个元组,可以使用._和索引来访问元组的元素(矢量的分量,注意和数组不同的是,元组的索引从1开始) scala> val pair = (22,"one") pair: (Int, String) = (22,one) scala> println(pair._1) 22 scala> println(pair._2) one 元组的实际类型取决于它的分量的类型,比如上边的pair的类型实际为Tuple2[Int,String],目前Scala支持的元组的最大长度为22,如果有需要,你可以扩展更长的元组 4) Set scala> var set = Set("a","b") set: scala.collection.immutable.Set[String] = Set(a, b) scala> set+="c" scala> set res8: scala.collection.immutable.Set[String] = Set(a, b, c) scala> println(set.contains("c")) true 缺省情况Set为Immutable Set,如果你需要使用可修改的集合类(Set类型),你可以使用全路径来指明Set,比如scala.collection.mutalbe.Set 5) Map Map的基本用法如下(Map类似于其他语言中的关联数据如PHP) scala> val roman =Map(1->"I",2->"II") roman: scala.collection.immutable.Map[Int,String] = Map(1 -> I, 2 -> II) scala> println(roman(1)) I scala> println(roman(2)) II 4:类与对象 1) 单例与伴生对象 scala比java更面向对象的一个方面是Scala没有静态成员,替代品是scala有单例对象(singleton object),当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象(companion object),你必须在同一个源文件里定义类和他的伴生对象,类被陈我给是这个单例对象的伴生类(companion class),类和他的伴生对象可以互相访问其私有成员,例如,一个源文件大致如下: object A{ //A 是一个单例对象 //Object A 与Class A同名,故 是class A 的伴生对象 } class A{ // 类A与单例对象A同名 // 故class 是 object A 的伴生类 //两者可以互相访问私有成员 } 定义单例对象不是定义类型(在Scala的抽象层次上说) 类和单例对象的一个差别是,单例对象不带参数,而类可以,因为你不能用new关键字实例化一个单例对象,你没机会传递给他参数,每个单例对象都被作为由一个静态变量指向的虚构类,synthetic class的一个实例来实现,因此他们与java静态类有着相同的初始化语法。Scala程序特别要指出的是:单例对象会在第一次被访问的时候初始化 不与伴生类共享名称的单例对象成为孤立对象:standlone object。最常见的就是程序入口: scala> object ObjecOps { | def main(args: Array[String]): Unit = { | println(args.length) | } | } defined object ObjecOps scala> var arr = Array("1","2") arr: Array[String] = Array(1, 2) scala> ObjecOps.main(arr) 2 2) 主构造器和辅助构造器 主构造器:每个类都有主构造器,且与类的定义交织在一起,主构造器的参数紧跟在类名之后,如 class helloworld(val hello:String,val world:String){...},主构造器的参数在传入时,就已经被编译成字段,并在构造对象时初始化传入,一个类若没有显式定义主构造器,自动拥有一个无参主构造器,若类中有直接执行的语句(非定义的方法、函数等),每次构造对象时皆会执行一次,不论是什么样的构造器类型,如: scala> class HelloWorld(val v1:String,val v2:String){ | println("Hello World") | val v3 = v1+v2 | } defined class HelloWorld scala> val one = new HelloWorld("welcome","thinkgamer") Hello World one: HelloWorld = HelloWorld@6afe925b scala> val two = new HelloWorld("My Name is","thinkgamer") Hello World two: HelloWorld = HelloWorld@1b35c4f8 辅助构造器:Scala类可以有任意多个构造器,辅助构造器的名称为this,在类中定义,辅助盗走阿七必须以一个主构造器活其他一定义的辅助构造器调用开始,例如: scala> class GYT{ | private var v1 = "" | private var v2 = "" | def this(m:String) { | this() | this.v1=m | } | def this(m:String,n:String) { | this(m) | this.v2 = n | } | def get(){ | println(v1+"\t" + v2) | } | } defined class GYT scala> var one = new GYT("A") one: GYT = GYT@3b2e9a90 scala> one.get() A scala> var one = new GYT("A","B") one: GYT = GYT@753e481 scala> one.get() A B 3) 嵌套类 scala允许任何语法结构中嵌套任何语法结构,因此能在类中定义类,例如: scala> class A{ | class B{ | println("this B") | } | println("this A") | } defined class A scala> val one = new A() this A one: A = A@7ee14288 对于同一个外部类,不同实例下的内部类是不同的,形如val one = new A() 与val two = new A(),one.B 和 two.B是两个不同的类 内部类中可以调用外部类的策划那个元,利用外部类.this或指针实现,例如: scala> class Hello{ | pointto=> | val value1 = "" | class Hi{ | val value2 = Hello.this.value1 | val value3=pointto.value1 | } | } defined class Hello scala> val one = new Hello one: Hello = Hello@25703a98 scala> val two = new one.Hi two: one.Hi = Hello$Hi@617599ab scala> two.value2 res4: String = "" scala> two.value3 res5: String = "" java的内部类(嵌套类是从属于外部类的,而scala的内部类是从属于对象的),再看一个实例加深对scala内部类的理解: scala> //定义外部类 scala> class Outer(val name:String){ | outer=> | class Inner(val name:String){ | def foo(b:Inner)=println("Outer" + outer.name+" Inner" + b.name) | } | } defined class Outer scala> val out1 = new Outer("Spark") out1: Outer = Outer@1fed28ce scala> val out2 = new Outer("Scala") out2: Outer = Outer@5f5fe5b9 scala> val in1 = new out1.Inner("hadoop") in1: out1.Inner = Outer$Inner@537d147e scala> val in2 = new out2.Inner("Hbase") in2: out2.Inner = Outer$Inner@7376d6c3 scala> in1.foo(in1) OuterSpark Innerhadoop scala> in2.foo(in2) OuterScala InnerHbase 4) apply方法 需要构造有参数需求的伴生对象时,可定义并使用apply方法,例如: scala> class Hello(var m:String,n:Char){ | println(m + "\t" + n) | } defined class Hello scala> object Hello{ | def apply(n:Char) = new Hello("|" ,n ) | } defined object Hello warning: previously defined class Hello is not a companion to object Hello. Companions must be defined together; you may wish to use :paste mode for this. scala> val hi = Hello('j') | j hi: Hello = Hello@111648d0 在创建实例时若使用new来创建则使用的是class,若直接使用雷鸣创建对象则使用的是object创建对象 5) 扩展 extends是Scala中实现继承的保留字,形如 class week extends month{...},week类继承了month类的所有非私有成员,在这里week是month的子类,month是week的超类,子类能够重写超类的成员(具有相同的名称和参数),另外单例对象同样能从类中继承,与类的继承语法相同 6) 重写 Scala中使用override保留字进行方法、字段重写,形如:class week extends month{ override def firstday = {...} } override保留字实际使用类似于private,声明这个保留字后的定义、声明是对超类的重写,因此,其也可以写在类定义的参数中,形如: class week(override val lastday:String) extends month{...} 重写包括字段和方法,单参数不同的方法可以不重写,例如 class month {def secondday (m:String) = {...} } class week extends month { def secondday = {...} } 进行重写时有以下一些规则: 重写def:用val,利用val能重写超类用没有参数的方法(getter) 用def,子类的方法与超类方法 用var,同时重写getter、setter方法,只重写getter方法报错 重写val:用val,子类的一个私有字段与超类的字段重名,getter方法重写超类的getter方法 重写var:用var,且当超类的var是抽象的才能被重写,否则超类的var都会被继承 看一个包含重写,继承和主构造器的例子: scala> class Month{ | var day = 0 | private var month = 0 | def this(now_day:Int,now_month:Int){ | this() | this.day = now_day | this.month = now_month | } | def add(){ | day+=1 % 30 | month += (day / 30) | println("day is:" + day + "\nmonth is:" + month) | } | } defined class Month scala> class Week extends Month{ | private var week = 0 | def this(now_day:Int,now_week:Int){ | this() | this.day = now_day | this.week = now_week | } | override def add():Unit={ | day+=1% 7 | week += (day/7) | println("day is:" + day + "\nweek is:" + week) | } | } defined class Week scala> var m1 = new Month(1,1) m1: Month = Month@2bda037e scala> m1.add day is:2 month is:1 scala> m1.add day is:3 month is:1 scala> var w1 = new Week(1,1) w1: Week = Week@442634e9 scala> w1.add day is:2 week is:1 scala> w1.add day is:3 week is:1 scala> m1.add day is:4 month is:1 7) 抽象 不能被实例化的类叫做抽象类,抽象类的某个或者某几个成员没有被完整定义,这些没有被完整定义的成员称为抽象方法或抽象字段,用abstract保留字标记抽象类,例如: scala> abstract class year{ | val name:Array[Srting] //抽象的val,带有一个抽象的getter方法 | var num:Int //抽象的var,带有抽象的getter/setter方法 | def sign //没有方法体/函数体,是一个抽象方法 | } 只要类中有任意一个抽象成员,必须使用abstract标记 重写抽象方法,抽象字段不需要使用override保留字 scala> abstract class animal{ | var name :String | var age : Int | def show | } defined class animal scala> class dog extends animal{ | override var name ="dog" | override var age = 23 | override def show(){ | println("name:" + this.name + "\tage: " + this.age) | } | } defined class dog scala> var d = new dog d: dog = dog@4a1c2a40 scala> d.show name:dog age: 23 //重写抽象方法不需要使用override保留字 scala> class pig extends animal{ | var name ="pig" | var age = 23 | def show(){ | println("name:" + this.name + "\tage: " + this.age) | } | } defined class pig scala> var p = new pig p: pig = pig@6bf5210 scala> p.show name:pig age: 23 8) 保护 当一个类不希望被继承、拓展时,可在类声明前加上final保留字,形如 final class year{...} 当一个类的某些成员不希望被重写时,可以在成员声明前加上final保留字,形如 class year {final def sign{...} } 当超类中的某些成员需要被子类继承,又不想对子类以外成员可见时,在成员声明前加上protected保留字,protected[this],将访问权限定于当前对象,类似于private[this] 类中protected的成员对其子类可见,对其超类不可见,形如:class year{ protected def sign {...} } “保护”的应用: 子类构造器的运行在超类构造器运行之后,在超类的构造器中调用的成员被子类重写后,返回值可能不正确,如下边的例子 scala> class month{ | val num = 31 | val days = new Array[Int](num) | } defined class month scala> class week extends month{ | override val num = 7 | } defined class week scala> val a = new week a: week = week@5c4e3bb0 scala> a.days res4: Array[Int] = Array() 我们可以看到 Array的长度变成了0,但是我们month类中明明定义为num长度,原因为何? 构造week对象前先执行超类month的构造器,num被初始化为31,month为初始化days数组,调用num,但是num被子类重写了,但因为week构造器还没被调用,此时num的值未被初始化,因而为0,days被设置为长度为0的数组,month构造器运行完毕,执行week构造器,num被初始化为7 解决办法:将超类的val声明为final,将超类的val声明为lazy scala> class month{ | final val num = 31 | val day = new Array[Int](num) | } defined class month scala> class week extends month{ | } defined class week scala> val w = new week w: week = week@3794d72e scala> w.day res10: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) scala> class month{ | lazy val num = 31 | val day = new Array[Int](num) | } defined class month scala> class week extends month{ | } defined class week scala> val w = new week w: week = week@183fde2b scala> w.day res11: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 另外一中办法是 在子类中使用提前定义语法,那么什么事提前定义? 提前定义是在超类的构造器运行之前初始化子类的字段,把需要提前定义的语句块放在extends与超类之间,并后接with保留字,形如 class week extends {override val num= 7} with month{...} 提前定义中=号右侧若需调用类B中成员时,除非B成员已在调用前提前定义 scala> class week extends{ | override val num = 7 | override val num2 = num +1 //允许,num已被提前定义 | override val num4= num2+num3 //不允许,num3没有在之前提前定义 | } with month{...} 9) 特质 Scala不支持多重继承,取而代之的是特质,一个子类只能拥有一个超类,一个超类能拥有多个子类,即class week extends month,year是不合法的,若一个子类继承自不同的超类,不同的超类中同名子类不知如何处理,多重继承会产生菱形继承问题,解决多重继承可能导致的问题消耗的资源远比多重继承产生的价值高 特质定义 Scala里代码复用的基础单元,封装了方法和字段的定义,一个类可以扩展自一个或多个特质,一个特质可以被多个类扩展,而且特质能限制被什么样的类所扩展,特质的定义使用的保留字是trait,具体语法和类定义相似,除了不能拥有构造参数,比如说我们来定义一个特质: scala> trait reset{ | def reset(m:Int,n:Int)=if(m>n) 1 | } defined trait reset 一旦特质被定义了,就可以混入类中 scala> class week extends reset{ | println("add class") | } defined class week 当要混入多个特质时,利用with保留字 scala> trait A{ | } defined trait A scala> trait B{} defined trait B scala> trait C{} defined trait C scala> class week extends A with B with C {} defined class week 特质的性质 成员时可以抽象的,而且不需要使用abstract声明,同样的,重写特质的抽象方法无需给出override,但是多个特质重写同一个特质的抽象方法需给出override,除了在类定义中可以混入特质,在特质定义中也可以混入特质 scala> abstract trait A{ | var a:Int | def show | } defined trait A scala> trait B extends A{ | override var a:Int | def show(){ | println() | } | } defined trait B 在对象构造时也可以混入特质,形如 val one = new month with resering 特质的构造也是有顺序的,从左到右被构造,构造器按超类->父特质->第一个特质->第二个特质(父特质不重复构造)->类 的顺序构造 如果 class A extens B1 with B2 with B3 ...那么串联B1,B2,B3...等特质,去掉重复项且右侧胜出 特质的应用 (1):接口,根绝已有的方法为类添加方法 实例,利用特质实现富接口,即构造一个具有少量方法和大量抽象方法的具体方法的特质,那么只要把特质混入类中,通过类重写抽象方法后,类便可以自动获取大量的具体方法 scala> trait Logger{ | def log(msg:String) | def warn(msg:String){} | def server(msg:String) {} | } defined trait Logger scala> class week extends Logger{ | def log(msg:String){ | println(msg) | } | server("test") | } defined class week (2) 为类提供可堆叠的改变(super保留字) 当为类添加多个互相调用的特质时,从最后一个开始进行处理,在类中super.foo()这样的方法调用时静态绑定的,明确是调用它的父类的foo()方法,在特质中写下了super.foo()时,它的调用是动态绑定的,调用的实现在每一次特质被混入到具体类的时候才被绑定,因此特质混入的次序的不同其执行效果也就不同 scala> abstract class IntQueue{ | def get():Int | def put(x:Int) | } defined class IntQueue scala> class BasicIntQueue extends IntQueue{ | private val buf = new scala.collection.mutable.ArrayBuffer[Int] | def get()=buf.remove(0) | def put(x:Int){ | buf += x | } | } defined class BasicIntQueue scala> trait Incrementing extends IntQueue{ | abstract override def put(x:Int){ | super.put(x+1) | } | } defined trait Incrementing scala> trait Doubling extends IntQueue{ | abstract override def put(x:Int){ | super.put(2*x) | } | } defined trait Doubling scala> object TestClient extends App{ | val queue = (new BasicIntQueue with Incrementing with Doubling) | queue.put(2) | println(queue.get()) | | val queue2 = (new BasicIntQueue with Doubling with Incrementing) | queue2.put(2) | println(queue2.get()) | } defined object TestClient PS:终于写完了,好累.....

优秀的个人博客,低调大师

Gfast 快速开发框架 V3.3.11 版发布

平台简介 基于全新 Go Frame 2.x+Vue3+Element Plus 开发的全栈前后端分离的管理系统 前端采用 vue-next-admin 、Vue、Element UI。 特征 高生产率:几分钟即可搭建一个后台管理系统 模块化:单应用多系统的模式,将一个完整的应用拆分为多个系统,后续扩展更加便捷,增加代码复用性。 插件化: 可通过插件的方式扩展系统功能, 目前插件有:工作流引擎、自定义表单、CMS、微信公众号、在线客服、商城系统、在线考试、资产管理、活动报名、在线点餐 等 认证机制:采用 gftoken 的用户状态认证及 casbin 的权限认证 自主研发:全部自主研发,功能完整可控面向接口开发 本次更新内容: 1.优化富文本编辑器弹层 2.添加上传组件前端判断文件合法性 3.修复添加菜单选择了角色报错 4.修复角色用户授权报错 5.优化后台主题切换经典布局分割菜单手机端 6.数据权限部门判断错误修复,新增数据权限按自定义接口路径判断权限 7.优化用户选择器数据权限 8、优化租户菜单分配,不同租户分配使用不同菜单 9、添加租户切换,若用户属于多个租户,登录后可在后台切换租户 演示地址http://v3.g-fast.cn/sys

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册