首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

ui2code中的深度学习+传统算法应用

作者:闲鱼技术-云听 背景 在之前的文章中,我们已经提到过团队在UI自动化这方面的尝试,我们的目标是实现基于 单一图片到代码 的转换,在这个过程不可避免会遇到一个问题,就是为了从单一图片中提取出足够的有意义的结构信息,我们必须要拥有从图片中切割出想要区块(文字、按钮、商品图片等)的能力,而传统切割算法遇到复杂背景图片往往就捉襟见肘了(见下图),这个时候,我们就需要有能力把复杂前后景的图片划分为各个层级图层,再交给切割算法去处理,拿到我们期望的结构信息。 经过传统切割算法处理,会无法获取图片结构信息,最终只会当成一张图片处理。 在业界,图片前后景分离一直是个很麻烦的命题,业界目前比较普遍采用的解决方案是计算机视觉算法提取,或是引入人工智能来解决,但直到现在,都没有百分百完美的解决方案。那是否能引入AI来解决这个问题呢,我们来看一下,目前使用

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

JavaScript学习(十四)---String对象中的模式匹配方法

目录 1.match()方法 语法:stringobj.match(rgExp) 例子: 2.search()方法 语法:stringobj.search(rgExp) 例子: 3.replace()方法 语法:replace(rgExp.replaceText) 例子: 4.split()方法 语法:split([separator[,limit]]) 1.match()方法 match()方法使用正则表达式模式对字符串进行查找,并将包含查找的结果作为数组返回。 语法:stringobj.match(rgExp) stringobj:必选项。对其进行查找的String对象或字符串文字 rgExp:必选项。为正则表达式模式和可用标志的正则表达式对象。也可以是包含正则表达式模式和可用标志的变量名或字符串文字。 如果match方法没有找到匹配,则返回null。如果找到匹配则返回一个数组并且更新全局RegExp对象的属性以反映结果。 match方法返回的数组有3个属性:input,index,lastindex。 如果没有全局标志(g),数组的0元素包含整个匹配,而第1-n元素包含了匹配中曾出现过的任一子匹配。这相当于没有设置全局标志,元素0-n中包含所有匹配。 例子: <script language="JavaScript"> function MatchDemo(){ var r,re; //声明变量 var s="I'm a good man"; re=/man/i; //创建正则表达式 r=s.match(re); //尝试匹配搜索字符串 return(r); //返回第一次出现"body"的地方 } document.write(MatchDemo()); </script> <script language="JavaScript"> function MatchDemo(){ var r,re; //声明变量 var s="I'm a man a good man"; re=/man/ig; //创建正则表达式 r=s.match(re); //尝试匹配搜索字符串 return(r); //返回第一次出现"body"的地方 } document.write(MatchDemo()); </script> 2.search()方法 search()方法返回与正则表达式查找内容匹配的第一个子字符串的位置。 语法:stringobj.search(rgExp) stringobj:必选项。对其进行查找的String对象或字符串文字 rgExp:必选项。为正则表达式模式和可用标志的正则表达式对象。也可以是包含正则表达式模式和可用标志的变量名或字符串文字。 例子: <script language="JavaScript"> function MatchDemo(){ var r,re; //声明变量 var s="I'm a man a good man"; re=/man/ig; //创建正则表达式 r=s.search(re); //尝试匹配搜索字符串 return(r); //返回第一次出现"body"的地方 } document.write(MatchDemo()); </script> 3.replace()方法 replace()方法使用表达式模式对字符串进行搜索,并对搜索后到的内容用指定字符串替代,返回一个字符串对象,包含替换后的内容。 语法:replace(rgExp.replaceText) rgExp参数为搜索时要使用的表达式对象。如果是字符串,不按正则表达式的方式进行模糊搜索,而是进行精确搜索。 replaceText参数为用于替换搜索到的内容的字符串,其中可以使用一些特殊的字符组合来表示匹配变量。其中,$&是整个表达式模式在被搜索字符串中所匹配的字符串,$是表达式模式在被搜索字符串中所匹配的字符串左边的所有内容,$‘是表达式在被搜索字符串中所匹配的字符串右边的所有内容,$$则是普通意义的“$”字符。 例子: <script language="JavaScript"> var strSrc="a13f58af4f41af"; var re=/(\d)(\d)/gi; var strDest=strSrc.replace(re,"$2$1"); document.write("字符串"+strSrc+"被转化为:"+strDest); </script> 4.split()方法 split()方法返回按照某种分割标识符将一个字符串拆分成若干个子字符串时所产生的子字符串数组。 语法:split([separator[,limit]]) separator是分割标识符参数,可以是多个字符或一个正则表达式,并不作为返回到数组元素的一部分。参数limit限制返回元素的个数。 <font size="+1"> <font face="宋体"> <script language="JavaScript"> var splitArray=new Array(); var string="JavaScript、ASP、JSP、Java"; var regex=/、/; splitArray=string.split(regex); for(i=0;i<splitArray.length;i++){ document.write(splitArray[i]+" "); } </script>

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

java8学习:并行数据处理与性能

内容来自《 java8实战 》,本篇文章内容均为非盈利,旨为方便自己查询、总结备份、开源分享。如有侵权请告知,马上删除。书籍购买地址:java8实战 在java7之前实现并行处理数据集合非常麻烦 得明确的把包含数据的数据结构分成若干子部分 要给每个子部分分配一个独立的线程 在恰当的时候对他们进行同步来避免不希望出现的竞争条件,等待所有线程完成,最后把结果汇总在一起 在java7引入了fork/join框架来实现并行,在这篇文章中,将介绍利用Stream来实现并行和所需要注意的事项,并且介绍fork/join框架 之前我们提到过stream()是顺序执行,而parallelStream()是并行执行,并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流,这样可以把压力分担给不同的内核 下面是一个例子:就是求和操作,从1加到给出的n,我们用顺序流实现一下 @Test public void test() throws Exception { long l = System.currentTimeMillis(); System.out.println(parallelSum(10_000_000)); //一千万相加 System.out.println(System.currentTimeMillis() - l); } public long sequentialSum(long n){ //237+ return Stream.iterate(0L, i -> i + 1) .limit(n) .reduce(0L,Long::sum); } 下面是并行流 public long parallelSum(long n){ //1533+ return Stream.iterate(0L, i -> i + 1) .limit(n) .parallel() //切换并行流 .reduce(0L,Long::sum); } 上面并行流将Stream内部数据分为几块,对不同的块进行归约操作后在汇总成最终结果 parallel是将顺序流转为并行流,而sequential是将并行流转为顺序流,你可能会想:利用这两个转换方法来使得更精确的控制流的,但是需要注意的是 Stream.iterate(0, i -> i + 1) .parallel() //如果贴到IDEA中,这会变灰的,也证明了它是无效的操作 .limit(n) .sequential() .reduce(0,Integer::sum); 上面只是一个例子,我们先转换为并行流然后经过操作在转换为顺序流,本以为可以精确控制流的,但最终stream是以顺序流的方式执行的,也就是说,最后调用的转换方法将影响整个stream 提到的并行流,那么他肯定需要多个线程,那么线程是从哪里来的?他有几个? 并行流内部使用了ForkJoinPool,它默认的线程数量就是自己使用的机器的处理器数量,可以通过Runtime.getRuntime().availableProcessors();得到,当然自己想改这个值的话,需要设置系统属性通过System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12");设置,一般是不需要修改的 测试一下性能 顺序流:上面已经测试了,时间在90ms+ fori long l = System.currentTimeMillis(); long sum = 0; for (int i = 0; i <= 10000000; i++) { sum += i; } System.out.println(sum); System.out.println(System.currentTimeMillis() - l); //7+ 并行流:上面已经测试了,时间在130ms+ 结果并不是我们期待的并行流更加快速,对于fori的结果的解释是:他的操作更加偏底层所以也更快,并且它是使用的基本类型避免了拆箱装箱的操作。提到装箱拆箱,这我们自然的就会想到变更上面顺序流和并行流中使用的Stream为基本类型Stream,测试如下 public long sequentialSum(long n){ //85+ return LongStream.iterate(0L, i -> i + 1) .limit(n) .reduce(0L,Long::sum); } public long parallelSum(long n){ //313+ return LongStream.iterate(0L, i -> i + 1) .limit(n) .parallel() .reduce(0L,Long::sum); } 结果已经提升了不少时间了,但是结果还是比较让人失望,因为相差fori还是有很大距离,那么我们下面就来详细的看一下为什么会发生这样的事情 上面Stream都是用到了iterate方法来生成数字,但是这存在问题 iterate生成的是装箱的对象,必须拆箱之后才能进行数字求和 很难把iterate分成多个独立的块来并行执行(必须意识到哪些流更容易让并行流来切分为子任务,iterate很难切分为子任务的原因是:每次应用iterate都会依赖上次生成的结果,因此如果切分成独立小块,那么他就不知道下一个会生成的是什么了(自己的理解。)) 我们之前学过一个生成数字范围的方法来我们来尝试一下,为什么要尝试这个?因为他直接生成一个数字范围并不依赖于之前的任何东西就可以生成,测试如下 public long sequentialSum(long n){ //62+ return LongStream.rangeClosed(0L,n) .reduce(0L,Long::sum); } public long parallelSum(long n){ //73 + return LongStream.rangeClosed(0L,n) .parallel() .reduce(0L,Long::sum); } 测试一亿数字相加结果:fori:43,stream:119,parallel:99.这时候并行流才会超过顺序流 好了到这基本就完事了,如果你购买了这本书,那么对于书上的并行流计算1千万,它达到的结果是1毫秒。但是对比机器我的是八核他仅仅是四核。所以对自己的测试产生了疑问,如果你现在正好看到这里,恳请您可以用自己的电脑测试一下,评论到下面:机器配置+各个的测试结果,对于您的评论将感激不尽 对于上面我们看到了,因为更改了一些api和一些基本类的stream而达到的提升是很惊人的,并行流从一秒多提升到73毫秒。所以下面将要说的是如何争取的使用并行流 对于并行中共享变量的演示 class A { public long total = 0; public void add(long value){ total += value; } } public long sum(long n){ A a = new A(); LongStream.rangeClosed(0,n).parallel().forEach(a::add); return a.total; } @Test public void test() throws Exception { System.out.println(sum(10_000_000)); } 如上的结果各式各样,原因就是total += value并不是原子性的,所以为了保证正确需要加同步,但是这又违反了并行原则。所以到这的结果就是在并行计算中避免使用共享变量 高效的使用流 下面的是书上给出的一些建议,对于流写完评价性能,请重复测试,以保证流是比较高效的 对于拆箱装箱的问题要意识到,如果有那么就有意识的使用api或者类给避免掉 对于较小的数据量不推荐并行流,因为并行流有拆分和合并的过程 考虑数据结构是否容易分解,比如上面的iterate就不容易分解,下面列出了常用的类和对应的可分解性 考虑终端操作中合并步骤的代价的大小,如果合并代价很大,那么组合每个子流产生的部分结果所付出的代价就可能会超过通过并行流得到的性能提升 注意一些api在流中的使用,比如limit和findFirst,因为他们依赖元素的顺序(并行流把数据拆分为几块,如果findFirst那么他就必须去寻找正在的数据的头元素,而不是每个子流中的任一的头元素),他们在并行流上使用代价很大,当然findAny就比findFirst快,因为随便一个子流返回一个就可以了,流中有一个操作unordered方法会将有序流转换为无序流,那么现在如果你需要流中的n个元素而不是专门要前n个元素的话,对无序并行流调用limit可能会比单个有序流更高效 考虑操作流水线的总计算成本:设N是要处理元素的数量,Q是元素通过流水线的大致处理成本,则N*Q就是这个对成本的一个估计,Q值较高就代表使用并行流的性能会更好(自己的理解:综合考虑元素合数和对每个元素处理的复杂度,复杂度越高就越推荐使用并行流,这样可以分摊到各个内核并行处理元素) | 源 | 可分解性 | | -------- |------- | | ArrayList | 极佳 | | LinkedList | 差 | | IntStream.range | 极佳 | | Stream.iterate | 差 | | TreeSet | 好 | | HashSet | 好 | 前面提到了并行流的实现就是使用了forkjoin框架,那么现在来了解一下forkjoin框架:移步到ForkJoin 了解完forkjoin,我们知道了在此框架中是如何的切分数据的,那么我们的并行流是怎么实现切分数据的呢? 能够帮助并行流切分数据的就是Spliterator机制:可分迭代器 Spliterator是java8新接口,可以用于遍历数据源中的元素,但它是为了并行执行而设计的,一般在开发中不需自己开发 接口定义 public interface Spliterator<T> { boolean tryAdvance(Consumer<? super T> action); Spliterator<T> trySplit(); long estimateSize(); int characteristics(); ... } 泛型是需要遍历的元素类型 tryAdvance:会按顺序一个个使用Spliterator中的元素,并且如果还有其他元素要遍历就返回true trySplit:专门为此接口设计的,它可以把一些元素划出去分给第二个Spliterator,由该方法返回,让两个Spliterator并行处理 estimateSize:用来估计还剩下多少元素要遍历,因为即使不那么确切,能快速算出来是一个值也有助于让拆分均匀一点 拆分过程 将Stream拆分的过程是一个递归的过程,先将最开始的Spliterator尝试trySplit拆分,生成第二个Spliterator2,然后再次对这两个Spliterator进行拆分...框架一直拆分Spliterator,直到Spliterator调用trySplit返回null,标示处理的数据结构已经不能分割,这时候此Spliterator上的拆分就终止了 拆分过程也受Spliterator本身的特性影响,而特性就是由上面定义的方法characteristics声明的 Spliterator特性 characteristics返回一个int,代表Spliterator本身特性集的编码。使用Spliterator可以通过特性来更好的控制和优化它的使用 | 特性 | 含义 | | ------------- | ------------- | | ORDERED | 元素有既定的顺序:List,因此Spliterator在遍历和划分的时候也会遵循这一规律 | | DISTINCT | 对于任意一对遍历过的元素x和y,x.equals(y)=false | | SORTED | 遍历的元素按照一个预定义的顺序排序 | | SIZED | 该Spliterator由一个一只大小的源建立:Set,因此 estimateSize返回的是准确值 | | NONNULL | 保证遍历的元素不会为null | | IMMUTABLE | Spliterator的数据源不能修改,就意味着在遍历时不能增删改任何元素 | | CONCURRENT | Spliterator的数据源可以被其他线程同时修改而无需同步 | | SUBSIZED | Spliterator和所有从它拆分出来的Spliterator都是SIZED | 实现自己的Spliterator 实现String字符串中有多少个单词 String str = "hello hello hellohello hello hello hellohello hello hello"; IntStream.range(0,str.length()).mapToObj(str::charAt).forEach(System.out::println); 如上代码结果就是str字符串被一个个输出,现在我们得到了这个流,那么我们现在就可以根据流来判断有多少个单词了,因为只要是" "那么就是遇到空格那么肯定就是一个单词出现了 现在我们需要两个值来保存状态:counter用来计算到目前为止数过的字数,还有一个Boolean记录上一个是否遇到的是空格,如下代码 public class WordCount { private final int counter;//单词累计 private final boolean lastSpace;//字符标志 public WordCount(int counter, boolean lastSpace) { this.counter = counter; this.lastSpace = lastSpace; } //接收流中的一个个char并判断 public WordCount accumulate(Character c) { if (Character.isWhitespace(c)){ //如果是true的话,那么就返回当前对象, return lastSpace ? this : new WordCount(counter,true); }else { //如果是true,那么就代表上一个字符是空格,那么就为下一个对象的累加计数器加一,并为字符标志器赋值为false代表遇到的不是空格 return lastSpace ? new WordCount(counter + 1 , false) : this; } } //合并方法:合并两个wordCounter public WordCount combine(WordCount wordCount){ return new WordCount(counter + wordCount.counter,wordCount.lastSpace); } public int getCounter() { return counter; } } accumulate定义了用哪个状态来建立新的WordCOunter,因为类中的变量是不可变的,每次遍历Stream中的一个新的Character时,就会调用此方法 combine会对作用与Character流的两个不同子部分的两个WordCounter的部分结果进行汇总,也就是将WordCounter内部计数器加起来 测试如上方法 //共用测试方法 private int countWords(Stream<Character> stream){ return stream.reduce(new WordCount(0,true),WordCount::accumulate,WordCount::combine).getCounter(); } String s = " hello hello hellohello hello hello hellohello hello hello "; Stream<Character> stream = IntStream.range(0, s.length()) .mapToObj(s::charAt); int i = countWords(stream); System.out.println("i = " + i); //顺序流并不会调用combine,因为它的累加在创建新的WordCounter类时已经累加了 我们看上面的测试方法,可以传入一个stream,如果让他调用转换为并行流的方法应该也行得通,比如 String s = " hello hello hellohello hello hello hellohello hello hello "; Stream<Character> stream = IntStream.range(0, s.length()) .mapToObj(s::charAt); int i = countWords(stream.parallel()); //注意变化 System.out.println("i = " + i); 但是失望的是结果是不对的,有个好消息就是我们知道了并行流的情况下,才会调用combine方法 结果为什么不对?因为你的字符串转换为并行流处理后,并行流并不清楚如何切分你的hello,可能把他且分为两个流中去了,比如A流有Hell,而o却跑到了B流中,以至于一个单词被算成了两个单词,我们要想办法避免并行流随机切分数据块,那么这时候就会用到Spliterator了 不让并行流随意切分的思路是:我们在切分数据的时候依旧是从数据的中间定位开始,就是index=length/2,但是我们要判断此处是否是空格,如果是的话那么直接切,不是的话,就要往前挪或者往后挪index,直到出现空格为止,所以实现代码如下 public class WordCountSpliterator implements Spliterator<Character> { private final String string; private int currentChar = 0; public WordCountSpliterator(String string) { this.string = string; } //会按顺序一个个使用Spliterator中的元素,并且如果还有其他元素要遍历就返回true @Override public boolean tryAdvance(Consumer<? super Character> action) { action.accept(string.charAt(currentChar++)); return currentChar < string.length(); } //可以把一些元素划出去分给第二个Spliterator,由该方法返回,让两个Spliterator并行处理 @Override public Spliterator<Character> trySplit() { int currentSize = string.length() - currentChar; if (currentSize < 10){ return null; } for (int splitPos = currentSize / 2 + currentChar; splitPos < string.length(); splitPos++) { if (Character.isWhitespace(string.charAt(splitPos))){ Spliterator<Character> spliterator = new WordCountSpliterator(string.substring(currentChar,splitPos)); currentChar = splitPos; return spliterator; } } return null; } //用来估计还剩下多少元素要遍历,因为即使不那么确切,能快速算出来是一个值也有助于让拆分均匀一点 @Override public long estimateSize() { return string.length() - currentChar; } //返回一个int,代表Spliterator本身特性集的编码。 @Override public int characteristics() { return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE; } } 代码逻辑并不难,不要被Spliterator和并行流吓住,拿本子画一画就很清楚明白了,比如切分是这样的 测试代码 String s = "hello hello hellohello hello hello hellohello hello hello"; WordCountSpliterator spliterator = new WordCountSpliterator(s); Stream<Character> stream = StreamSupport.stream(spliterator, true); int i = countWords(stream); System.out.println("i = " + i); Spliterator代码解释 tryAdvance把String中当前位置的Character传给了Consumer,并让位置加一。这里只有一个归约函数即WordCounter类的accumulate方法。如果新的指针位置小于String的总长,且还有要遍历的Character,则tryAdvance返回true trySplit定义了拆分要遍历的数据结构的逻辑。在方法中首先定下了什么时候不在拆分,不拆分的时候返回null,需要拆分的时候,就把试探的拆分为止设在要解析的String字符串的中间,如果不是空格就往后找,避免把String错切分成两个单词。一旦找到空格那么就创建一个新的Spliterator来遍历从当前位置到拆分位置的子串,把当前位置this设置为拆分位置,因为之前的部分将由新Spliterator处理最后返回 还需要遍历的元素的estimatedSize就是这个Spliterator解析的字符串的总长度和当前遍历的位置的差 characteristics告诉框架这个SPliterator是 ORDERED:顺序就是String各个char的次序 SIZED:estimateSize方法的返回值是精确的 SUBSIZED:trySplit方法创建的Spliterator也有确切大小 NONULL:字符串中不能有null IMMUTABLE:在解析字符串时不能再添加数据,因为字符串不可变 Spliterator最后需要注意的一个功能是:就是可以在第一次遍历,第一次拆分或第一次查询估计大小时绑定元素的数据源,而不是在创建时就绑定

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

Python机器学习方法智能识别亚马逊验证码

概述 亚马逊网站验证码全部由英文字母组成,每个字母的形式也是多样的,通过Tesseract-OCR技术识别效率还是比较低,非常不理想。这里采用向量空间技术进行训练识别,经测试,识别率可达到95%,这个识别率通过训练库的不断增加还可继续提高。下面废话不多说,直接上干货。 技术详解 亚马逊验证码如下图: 我这里收集了大量的亚马逊网站验证码,下面将随机抽一张验证码为mnyaph作详细讲解,如下图: 总体思路 1.将原图片作二值化等特殊处理转换得到低像素图片 2.分割出每个字母的图片,并加入到训练库中 3.每个字母图片在训练库中训练 4.将每个字母图片训练后的结果依次组合起来,就是最终验证码 使用技术库 PIL :图片处理库 scipy : 科学计算库 ● 原图片处理 这里将jpg格式转换为更小容量的gif格式,方便后面处理,并将原图片中的黑色像素(0)拷贝到新

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

19.Swift学习之构造函数与析构函数

重要说明 本文中提到的构造函数,在很多书中有其他的说法,如构造器,构造方法,初始化,初始函数等 本文中提到的析构函数,在很多书中有其他的说法,如反构造器,析构方法,反初始化,反初始函数等 构造函数的介绍 构造函数用于初始化一个类的实例(创建对象) 默认情况下载创建一个类时,必然会调用一个构造函数 即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数 如果是继承自NSObject,可以对父类的构造函数进行重写 默认构造函数 在创建类和结构体的实例时必须为所有的存储属性设置一个合适的初始值,如果不是在定义时初始化值,可以在构造函数中赋值 构造函数就像一个没有形式参数的实例方法,使用 init 关键字来写 class Person { var name:String var age:Int var sex:String //1.构造函数没有func修饰 //2.构造函数默认完成调用 不能手动调用 init() { print("被调用") name = "Zhangsan" age = 10 sex = "male" } } var p = Person() p.age p.name p.sex 自定义构造函数 希望在创建一个对象时手动给属性赋值(属性的值是在外面传进去的) 可以自定义构造函数 自定义构造函数和默认构造函数可以同时存在 //定义类 class Person { var name:String var age:Int var sex:String //默认构造函数 init() { print("被调用") name = "Zhangsan" age = 10 sex = "male" } //自定义构造函数 init(name:String, age:Int, sex:String) { self.name = name self.age = age self.sex = sex } } var p = Person() p.age p.name p.sex var p2 = Person(name: "Wangli", age: 11, sex: "female") p2.age p2.name p2.sex 结构体类型的成员构造函数 如果结构体类型中没有定义任何自定义构造函数,它会自动获得一个成员构造函数 //定义了一个名为 Size 有两个属性分别是 width 和 height 的结构体,这两个属性通过分配默认值 0.0 ,从而被推断为 Double 类型 struct Size { var width = 0.0, height = 0.0 } //Size 结构体自动接收一个 init(width:heght:) 构造函数 let twoByTwo = Size(width: 2.0, height: 2.0) 值类型的构造函数委托 构造函数可以调用其他构造函数来执行部分实例的初始化。这个过程,就是所谓的构造函数委托 构造函数委托对于值类型和类类型是不同的。 值类型(结构体和枚举)不支持继承,所以它们的构造函数委托的过程相对简单 注意如果为值类型定义了自定义构造函数,就不能访问默认构造函数或者是成员构造函数 struct Size { var width = 0.0, height = 0.0 init() { //构造函数委托 self.init(width: 2.0, height: 2.0) } init(width:Double, height:Double) { self.width = width self.height = height } } //要么不要写任何构造函数,要么全写所有的构造函数,否则下面第二种调用方式会有问题,参考上面第四条 var size = Size() size.width size.height var size2 = Size(width: 1.2, height: 1.2) size2.width size2.height 类的继承和初始化 所有类的存储属性——包括从它的父类继承的所有属性都必须在初始化期间分配初始值。 Swift 为类类型定义了两种构造函数以确保所有的存储属性接收一个初始值,它们就是指定构造函数(Designated Initializer)和便捷构造函数(Convenience Initializer) 指定构造函数是类的主要构造函数。指定构造函数可以初始化所有类引用的属性并且调用合适的父类构造函数来继续这个初始化过程给父类链 一个类通常只有一个指定构造函数并且每个类至少得有一个指定构造函数 便捷构造函数是次要的,可以在相同的类里定义一个便捷构造函数来调用一个指定构造函数给指定构造函数设置默认形式参数 //类的指定构造函数 init(parameters) { statements } //便捷构造函数有着相同的书写方式,但是要用 convenience 修饰符放到 init 关键字前,用空格隔开 convenience init(parameters) { statements } 类类型的构造函数委托 为了简化指定和便捷构造函数之间的调用关系,Swift 在构造函数之间的委托调用有下面的三个规则: 规则 1——指定构造函数必须从它的直系父类调用指定构造函数 规则 2——便捷构造函数必须从相同的类里调用另一个构造函数(可以是指定也可以是便捷) 规则 3——便捷构造函数最终必须调用一个指定构造函数 简单记忆的这些规则的方法如下: 指定构造函数必须总是向上委托。 便捷构造函数必须总是横向委托。 类类型的构造函数委托 class Car{ var speed:Double //Designated Initializer init(speed:Double) { self.speed = speed } convenience init(){ self.init(speed: 60.0) } } class Bus : Car { var wheels : Int init(wheels: Int) { self.wheels = wheels //由于子类继承了父类中的存储属性 所以必须借助父类的指定构造函数来初始化继承的那个存储属性的值 //一定要在子类的属性初始化完毕以后调用 super.init(speed: 120.0) } convenience init(){ self.init(wheels: 6) } } 构造函数的继承与重写 在Swift中,子类的构造函数有两种来源,首先是自己拥有的构造函数,其次是从父类中继承过来的构造函数。但是,并不是所有父类构造函数都能够被子类继承。子类继承父类的构造函数是有条件的,遵守以下2个规则: 规则1——如果子类没有定义任何指定构造函数,它会自动继承父类所有指定构造函数 规则2——如果子类提供了所有父类指定构造函数的实现(通过规则1继承来的或者提供自定义实现的),那么它会自动继承所有父类便捷初始化器 如果一个子类中任意的构造器和父类的便利构造器一模一样, 不算重写 class Person { var name: String! var weight: Double // 普通自定义构造函数 init(name: String) { self.name = name self.weight = 0.0 } // 定义指定构造函数 init(name: String, weight: Double) { self.name = name self.weight = weight } // 定义便利构造函数 convenience init(n name: String, w weight: Double) { // 便利构造函数必须调用同类中的指定构造函数 self.init(name: name, weight: weight) } convenience init(showStr: String) { self.init(name: "", weight: 0.0) print(showStr) } } class Man: Person { var sex: String = "男" override init(name: String) { super.init(name: name) self.name = name self.weight = 0.0 } override init(name: String, weight: Double) { self.sex = "女" // 子类的指定构造函数中必须调用父类的构造函数 // 重写的时候,必须将调用父类的构造函数语句放在调用父类属性的前面 super.init(name: name, weight: weight) self.name = name self.weight = weight } // 定义构造函数与父类的便利构造函数一样, 这里不算重写 convenience init(showStr: String) { self.init(name: "", weight: 0.0) print(showStr) } } var manA = Man(name: "ZhangSan", weight: 62.0) var manB = Man(showStr: "Hello Swift") 可失败的构造函数 定义类、结构体或枚举初始化时可以失败 失败可能由以下几种方式触发,包括给初始化传入无效的形式参数值,或缺少某种外部所需的资源,又或是其他阻止初始化的情况 为了处理这种可能,在类、结构体或枚举中定义一个或多个可失败的构造函数。通过在 init 关键字后面添加问号init? 可失败的构造函数里面应该有一个 return nil 的语句(虽然没有也不报错) 通过可失败的构造函数构造出来的实例是一个可选型 struct Animal { let species: String init?(species: String) { // 返回一个nil if species.isEmpty { return nil } self.species = species } } //返回的类型是当前类型的可选型 let cat = Animal(species: "CAT") if let cat = cat { cat.species } let dog = Animal(species: "") if let dog = dog { dog.species } 必要构造函数 在类的构造函数前添加required 修饰符来表明表明它是一个必要构造函数 当子类重写父类的必要构造函数时,必须在子类的构造函数前也要添加 required 修饰符以确保当其它类继承该子类时,该构造函数同为必要构造函数 在重写父类的必要构造函数时,不需要添加 override 修饰符 class SomeClass { required init() { } } class SomeSubclass: SomeClass { required init() { } } 析构函数 Swift 会自动释放不再需要的实例以释放资源 Swift 通过自动引用计数(ARC)处理实例的内存管理 当引用计数为0时,系统会自动调用析构函数(不可以手动调用) 通常在析构函数中释放一些资源(如移除通知等操作) 析构函数的写法 //后面连()都没有 deinit { // 执行析构过程 } 示例练习 class Person { var name : String var age : Int init(name : String, age : Int) { print("Person-init") self.name = name self.age = age } deinit { print("Person-deinit") } } var person : Person? = Person(name: "Zhangsan", age: 18) person = nil

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

微服务重要的容器Docker学习系列十~高级操作

这个命令是我们经常使用来展示容器信息。 docker ps [OPTIONS] OPTIONS 可选参数: ●-a :显示所有的容器,包括未运行的。●-f :根据条件过滤显示的内容。●--format :指定返回值的模板文件。●-l :显示最近创建的容器。●-n :列出最近创建的n个容器。●--no-trunc :不截断输出。●-q :静默模式,只显示容器编号。 ● -s :显示总的文件大小。 我经常用的是 -a 或者不指定参数,也可以使用最近创建的几个容器 -n。 列子如下: 1docker@ubuntu:~$ docker ps 2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3fc8e73a8b48b mongo "docker-entrypoint.s…" 5 seconds ago Up 3 seconds 27017/tcp test-mongo 404d57e7380bf redis "docker-entrypoint.s…" 22 hours ago Up 2 hours 6379/tcp test-redis 5 6docker@ubuntu:~$ docker ps -a 包含创建好 没有启用的 7CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8fc8e73a8b48b mongo "docker-entrypoint.s…" 21 seconds ago Up 19 seconds 27017/tcp test-mongo 9f93712639869 redis "docker-entrypoint.s…" 22 hours ago Created test-redis1 1004d57e7380bf redis "docker-entrypoint.s…" 22 hours ago Up 2 hours 6379/tcp test-redis 11 12docker@ubuntu:~$ docker ps -s 13CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE 14fc8e73a8b48b mongo "docker-entrypoint.s…" About a minute ago Up About a minute 27017/tcp test-mongo 0B (virtual 380MB) 1504d57e7380bf redis "docker-entrypoint.s…" 22 hours ago Up 2 hours 6379/tcp test-redis 0B (virtual 83.4MB) inspect 用来获取容器或者镜像的元数据 docker inspect [OPTIONS] NAME OPTIONS 说明: ●-f : 指定返回值的模板文件●-s: 显示总的文件大小 ● --type: 为指定类型返回JSON数据 列子: 内容太多,粘贴一部分,大家自己尝试的时候可以多看看。获取容器信息 有很多的。也可以利用-f指定返回内容 1docker@ubuntu:~$ docker inspect test-redis 2[ 3 { 4 "Id": "04d57e7380bf130ecaeca455340a9f18e3c40679c299a5f9b12b8842709e4305", 5 "Created": "2018-08-31T15:44:24.634464326Z", 6 "Path": "docker-entrypoint.sh", 7 "Args": [ 8 "redis-server" 9 ], 10 "State": { 11 "Status": "running", 12 "Running": true, 13 "Paused": false, 14 "Restarting": false, 15 "OOMKilled": false, 16 "Dead": false, 17 "Pid": 2828, 18 "ExitCode": 0, 19 "Error": "", 20 "StartedAt": "2018-09-01T11:15:06.016924571Z", 21 "FinishedAt": "2018-09-01T04:11:15.077538199-07:00" 22 }, 23 "Image": "sha256:4e8db158f18dc71307f95260e532df39a9b604b51d4e697468e82845c50cfe28", 24 "ResolvConfPath": "/var/lib/docker/containers/04d57e7380bf130ecaeca455340a9f18e3c40679c299a5f9b12b8842709e4305/resolv.conf", 25 26 27docker@ubuntu:~$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' test-redis 28172.17.0.2 top 查看容器中运行的进程信息,跟linux中的top类似, 并且也支持ps命令参数。 docker top [OPTIONS] 容器 [ps OPTIONS] 例子: 1docker@ubuntu:~$ docker top test-redis 2UID PID PPID C STIME TTY TIME CMD 3999 2828 2806 0 04:15 pts/0 00:00:14 redis-server *:6379 attach 用来链接到正在运行中的容器 docker attach [OPTIONS] name(容器名字) OPTIONS: ● --sig-proxy=false 例子: 1docker@ubuntu:~$ docker attach test-redis 2^C1:signal-handler (1535809783) Received SIGINT scheduling shutdown... 31:M 01 Sep 13:49:44.031 # User requested shutdown... 41:M 01 Sep 13:49:44.033 * Saving the final RDB snapshot before exiting. 51:M 01 Sep 13:49:44.041 * DB saved on disk 61:M 01 Sep 13:49:44.042 # Redis is now ready to exit, bye bye... 7docker@ubuntu:~$ docker ps 8CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9fc8e73a8b48b mongo "docker-entrypoint.s…" 11 minutes ago Up 11 minutes 27017/tcp test-mongo 10docker@ubuntu:~$ docker attach --sig-proxy=false test-redis 11You cannot attach to a stopped container, start it first 12docker@ubuntu:~$ docker start test-redis 13test-redis 14docker@ubuntu:~$ docker attach --sig-proxy=false test-redis events 从容器中获取实时事件 docker events [OPTIONS] OPTIONS: ●-f:根据给定的条件进行过滤。 ●--since:根据指定的时间戳显示事件 例子: 1docker@ubuntu:~$ docker events --since="1525810182" 22018-09-01T04:15:04.930155641-07:00 network connect 702b1e16448dbb8fc6e0baf8d6a66d3c6c4c214ce2cf0e4281ab5d8826872099 (container=04d57e7380bf130ecaeca455340a9f18e3c40679c299a5f9b12b8842709e4305, name=bridge, type=bridge) 32018-09-01T04:15:05.020352568-07:00 volume mount 05911ac2f739e8d85206f0170cf26f066372a4e49b7a964202ff56dbe7325c6d (container=04d57e7380bf130ecaeca455340a9f18e3c40679c299a5f9b12b8842709e4305, destination=/data, driver=local, propagation=, read/write=true) 42018-09-01T04:15:06.088232843-07:00 container start 04d57e7380bf130ecaeca455340a9f18e3c40679c299a5f9b12b8842709e4305 (image=redis, name=test-redis) 52018-09-01T06:38:19.267367732-07:00 container create fc8e73a8b48be47f6fc59607506d4e78f0f8fe6a09a6f8c2b030c6cf5fc23264 (image=mongo, name=test-mongo) 62018-09-01T06:38:19.345489171-07:00 network connect 702b1e16448dbb8fc6e0baf8d6a66d3c6c4c214ce2cf0e4281ab5d8826872099 (container=fc8e73a8b48be47f6fc59607506d4e78f0f8fe6a09a6f8c2b030c6cf5fc23264, name=bridge, type=bridge) 72018-09-01T06:38:19.424505893-07:00 volume mount 3cbb0b38feb2d121bf065076e7354d59804ce7df091158addf3edf837d406a44 (container=fc8e73a8b48be47f6fc59607506d4e78f0f8fe6a09a6f8c2b030c6cf5fc23264, destination=/data/configdb, driver=local, propagation=, read/write=true) 82018-09-01T06:38:19.424512355-07:00 volume mount 4509ec37637d27647f8a98a1678318200bf772ae294bbc341b765fa606b20633 (container=fc8e73a8b48be47f6fc59607506d4e78f0f8fe6a09a6f8c2b030c6cf5fc23264, destination=/data/db, driver=local, propagation=, read/write=true) logs 查看容器的日志 docker logs [OPTIONS] name(容器名字) OPTIONS: ●-f:跟踪日志输出●--since:显示某个开始时间的日志●-t :显示时间戳●--tail: 列出需要的最新N条容器日志 例子:使用的时候最好带上参数这样,能显示出需要的日志,筛选掉不必要的日志内容 1docker@ubuntu:~$ docker logs test-redis 21:C 31 Aug 15:44:25.125 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 31:C 31 Aug 15:44:25.125 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=1, just started 41:C 31 Aug 15:44:25.125 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf 5 _._ 6 _.-``__ ''-._ 7 _.-`` `. `_. ''-._ Redis 4.0.11 (00000000/0) 64 bit 8 .-`` .-```. ```\/ _.,_ ''-._ 9 ( ' , .-` | `, ) Running in standalone mode 10 |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 11 | `-._ `._ / _.-' | PID: 1 12 `-._ `-._ `-./ _.-' _.-' 13 |`-._`-._ `-.__.-' _.-'_.-'| 14 | `-._`-._ _.-'_.-' | http://redis.io 15 `-._ `-._`-.__.-'_.-' _.-' 16 |`-._`-._ `-.__.-' _.-'_.-'| 17 | `-._`-._ _.-'_.-' | export 将文件系统作为一个tar归档文件导出到STDOUT.我们使用导出的时候,相应的也会用到导入 docker export [OPTIONS] 容器id OPTIONS: ●-o 将输入内容写到文件 例子: 1docker@ubuntu:~$ docker export -o test-redis.tar test-redis 2docker@ubuntu:~$ ls 3Desktop Downloads mongo.tar Pictures Templates Videos 4Documents examples.desktop Music Public test-redis.tar port 列出指定的容器的端口映射信息,或者查找面向公众的端口 docke port [OPTIONS] 容器名字或者id也可 例子: 1docker@ubuntu:~$ docker port test-redis 26379/tcp -> 0.0.0.0:6379 wait 阻塞运行直到容器停止,然后打印出退出的代码。 docker wait 名字 或者容器id 例子: 1docker wait test-redis 原文发布时间为:2018-09-29 本文作者:琪琪 本文来自云栖社区合作伙伴“LuckQI”,了解相关信息可以关注“LuckQI”。

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

SQL Serever学习17——数据库的分析和设计

数据库的分析和设计 设计数据库确定一个合适的数据模型,满足3个要求: 符合用户需求,包含用户所需的所有数据 能被数据库管理系统实现,如sqlserver,oracle,db2 具有比较高质量,容易理解,使用方便,便于维护,效率高 设计步骤分为6步: 需求分析,与用户沟通,达成统一意见 概念结构设计,创建E-R图 逻辑结构设计,从E-R图转为关系模型,1对多,多对多,建立数据模型,数据库三范式 物理结构设计,确定数据类型,是否可空,确定主键,外键,索引 数据库实施 数据库运行维护 数据库的三范式: 1NF,每个属性不可在分割,比如地址如果有省,市,那么还可以在分为省属性,城市属性 2NF,满足1NF前提下,每个非主键属性都依赖于主键,比如员工表(主键员工Id)的字段有部门Id和部门主管(依赖于部门Id,而不是员工Id),那么就要去掉部门主管字段 3NF,满足2NF前提下,非主键属性不能是其他字段的函数传递值,比如员工表的奖金字段=薪资字段X20%,那么就不符合3NF,应该去掉奖金字段 数据库系统开发 使用visual studio 2012工具,使用C#开发语言,创建有关销售管理数据库的windows应用系统。 ADO.NET介绍 是统一数据容器类编程接口,包含了2个核心: .NET Framework数据提供程序,为数据处理和快速访问数据设计的组件,有4个对象(Connection,Command,DataReader,DataAdapter) DataSet,看做内存中的数据源,将数据缓存到本地,进行数据的处理,不需要占用连接,可以释放连接给其他客户使用 使用ADO.NET开发数据库应用程序的步骤: 根据使用的数据源,确定.NET Framework数据提供程序(SQL Server,OLE DB , ODBC Oracle) 建立数据源连接,Connection对象 执行SQL操作,Command对象 获取数据,DataReader对象,DataSet对象 展示数据 使用ADO.NET连接数据库 自动配置数据库

资源下载

更多资源
腾讯云软件源

腾讯云软件源

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

Spring

Spring

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

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

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

用户登录
用户注册