研究生的第一个假期作业终于在今天结束了。寒假作业评比一共三轮，一共7个组，我们拿到了第一名，小组奖金900元虽然金额不大，也算是没有辜负我们组所有人这么长时间花费的努力。所以，简单记录一下这个寒假的工作吧~接收到任务1月10号学校放假，那时候新型冠状病毒还并不是一个新闻。我以为这又是一个枯燥的寒假。本来我们的寒假期间是1月10号~2月5号（正月十二）所以虽然我们腊月都感到截止日期的逐渐逼近，但我们还都是心照不宣的不讨论寒假作业这件事情。到了正月初四，组长终于开始意识到“问题很大”。于是组长贴心的下午两点叫我们起床，开工。那个时候虽然只是听说疫情挺严重，学校的通知也只是正月15之前禁止到校，但谁也不知道疫情竟然已经如此严重了，延迟开学居然延迟3月份还未能开学。[album type="photos"][/album]1月28号，正月初四，如果按照既定的时间截止的话，时间已经很紧急了，但是我丝毫没有开始写代码的欲望，甚至还有一种莫名的自信。但是组长的任务安排也都是有更近的截止时间，所以每天下午3点左右简直就是灵魂拷问时间啊2月4号之后，公司那边要求2月7号提供项目目前的进度报告：所以我们必须在2月6号左右就得完成一个大概的初始版本。也就是至少客户端可以直播对话。需要认真起来了然后这个时候，我对整个项目的流程都不是很清楚，拉流推流啥意思。recorder、grabber是啥。player、client、capture关系又是什么，我真的不太清楚。关于我们这次视频会议，公司之前在直播这方面有所研究，一直开发维护theia项目，这个项目的功能就是主播直播，观众观看、发弹幕、连线之类的。不管怎样，到了2月7号晚上我们基本有一个可以用的版本了。界面啥的基本都是沿用theia项目，我们为了简化流程，客户端拉流的只拉了一个多个用户的混合流。就这样有一周暂时没怎么写寒假作业，2月15号我觉得学一下javafx，重新写一下客户端这边界面，现在的界面颇有一股复古的气息。界面重构以及流程梳理本科的时候用netbeans写过java的桌面端程序，netbeans是可以直接拖控件就创建好界面的。现在使用Scala+javafx，于是学习了screnbuilder的用法还有jxml的用法，以及在网上找到了一个Scala+javafx的helloworld demo。到了19号我基本把界面重构出来，也把整个客户端的流程重新设计了一下，整个client模块我基本是重新写了一遍。到了2月25号的时候，对照寒假作业的要求，发现我们还有一些功能没有完成：这几个功能让整个会议的功能变得复杂了很多。除此之外，之前client只拉一个混合后的流的做法虽然简单，但是问题是显而易见的，延迟太高，5、6s左右，而且用户自身的画面和声音在混合流中，都有延迟，就很奇怪。所以，最后我们还是决定客户端拉多个用户的流，这样问题是增加了客户端这边的内存开销，但是效果是延迟更低，会议效果会更好。不仅如此，在最后的几天根据发言人、支持人身份对客户端的画布进行动态分配。写代码是一回事，等真正一起联调测试的时候，bug就是一个个的浮现水面了。准备最后的评选2月28号，我们开始了第一次测试，当时由于我开了全局代理，导致 new Image(url) 这个地方无法对图片进行加载，以至于我一打开会议界面就卡死了。所以第一次测试只有我们组的另外两个人。当时测试问题还比较少。到了29号晚上3个人测试，问题就变得特别多，不仅客户端这边，后台那边对ws消息的处理也有一些问题。3月1号下午我们又开始了测试，这才基本没什么影响使用的大功能了。3月2号开始了第一轮评比，7进3，我们录制一个4~5分钟视频发到视频网站上，然后研二的学长学姐们评比3月4号上午开始第二轮，3进2，同样是录制一个1~2分钟的视频发到视频网站上，然后参赛其他组评比3月4号下午开始进行第三轮，2进1，是参赛其他组直接观看web端的录像（混流后的）进行评比。由于我们客户端界面好看，第二轮我们录制的视频还特意做了一个小剧本，配上朱一旦的「完美搭档」的bgm，第三轮我们web查看的录像的混流效果很好，延迟较低，而且音画基本同步。所以最后，我们拿到了第一名。从一开始我们感觉好像来不及，到最后我们每个人都是朝着第三轮的目标开始完善自己的部分。也算是没有辜负我们的努力吧~最后的完成的界面：同时还有web端可以查看和管理录像。相关技术和工具：scalajavafxsrs 流服务器ffmepeg项目地址：https://github.com/HiddenTrackMJ/geek有兴趣欢迎star,fork~
苹果IOS/Ipad OS 10.13 OSX10.15 全系列更新后，有一个内置软件变化很大，那就是「提醒事项」。使用Android设备，尝试了无数款规划类的应用，比如什么四象限类型的。但没能坚持下来规划任务。但是Microsoft Todo 还是留在了我的Android手机上，虽然也没怎么使用。换了iphone之后，也是在研究生生活中，每天其实有很多琐碎的事情，如果不记录下来，简直一团糟（和本科混吃等死很不一样）ToDo 在iPhone上同样适用，与此同时微软时隔几年之后在Mac也上架了ToDo,使得这款应用成为一个跨平台的利器。但随着不断使用中，发现了一些我很不喜欢的地方，新版本的「提醒事项」，有着ToDo的影子，所以考虑是否切换到该内置应用。下面是两款软件的对比，也可以给大家做一些参考：ToDo设计问题「我的一天」「我的一天」这个微软介绍的截图中的“特性”。但却很有问题。对任意一个任务可以选择「添加到我的一天」，但是「我的一天」和「已计划的日程」却是完全独立的。「我的一天」里面的任务却不会出现在「已计划的日程」，除非你再次给这个日程添加一个「截止日期」/「提醒日期」为今天。这看起很复杂。如果我需要安排今天的任务，而且希望今天的任务也出现在「已计划的日程」中，则必须操作两次……对于「提供我的一天的建议」这个功能似乎很酷，但，其实就是显示一些未完成的清单列表嘛…… 我几乎没使用过这个功能。「任务」到底是什么任务？很早，ToDo 是没有文件夹分类清单，只能添加清单一级。所以「任务」这一栏就显示了所有的任务。后来增加了清单文件夹📂这一个功能，「任务」里面只会显示“未归类的任务”。对于我来说，「任务」这一栏就是空的，因为我所有的任务都会归属某个清单。这个功能就很……截止日期和提醒日期对于每个任务，ToDo 有两个时间设置。看起来，这两个时间的定义不同，但是实际操作起来却没什么必要。一个任务提醒你需要去完成了，为什么这个时间点不是截止时间呢。除非这个任务是时间跨度很长。但非常不建议添加这样模糊的任务到清单中。每一个任务尽量是具体的、可以很快执行完毕的，如果是长期任务一定要进行划分，否则这样的模糊的任务很大可能是无法进行的。我在使用ToDo的时候设置这两个时间完全就是跟随心情，有时候点截止日期，有时候点提醒日期，这让我很迷茫，尽管这两种方式都会让任务出现在「已计划日程」中。容易误触「已完成」在IOS上，如果选择「隐藏已完成任务」，在任务清单中，选中 一项未完成的任务 的「完成按钮」，这项任务就会立即消失在这个列表中。天知道，我已经很多次的误触这个「完成按钮」，导致不知道把哪个任务不小心给点「已完成」了。正确的体验应该是，点击已完成，不会立即消失，而是再次刷新列表的时候再去消失这一项。优点优点也有一些。跨平台，Android、IOS，Mac，Linux（虽然用不到）。但是我偶尔会用我的旧Android手机睡觉前看新闻（嘿嘿就是focus软件），偶尔也会记录一些突然想起来的任务。动画很不错。很多操作都有动画，比如任务消失，显示在清单列表中，列表的显示等排序的方式很多一些小细节做的比较完善一点，我喜欢的是这个：选择「提醒时间」的时候，会有「今日晚些时候」，而对于「明天」、「下周」这样的选项也会有一个默认的时间（不仅仅是日期）。因为很多时间，我的任务并不是精确到某个时间点，而是某天内完成都可以的，自己再去选择一个时间就有点麻烦和纠结了。对于今天超时的任务，选择「今日晚些时候」就很方便的再拖一会儿。提醒事项设计优点几乎ToDo的设计缺陷（我认为的）都修正了。「今天」的任务列表是“提醒时间为今天”的任务列表，没有对任务进行「添加到今天」这一步多余步骤。「全部」类似于ToDo的「任务」的功能，但是这里会“按照清单”分别显示所有的任务只有提醒日期一个时间设置，更简单IOS上，对任务点击已完成，不会立即消失。（但是Mac上也会立即消失，好在可以command+z 撤销操作）上面最膈应我的四个问题，「提醒事项」都修复了。除此之外还有一些亮点：能够对自然语言进行解析，这个Android 版本的Todo也是支持的，但是到了苹果平台就没有了，可能还来得及迭代。和电脑端可以使用「接力」任务提醒自动关联日历的日程中会在日历📅的小部件中显示，系统提醒非常及时和准确。但自身仍然也有一些问题。缺点提醒我，需要手动指定一个时间没有想ToDo那样的默认时间，就有点麻烦了！没有指定时间的任务如果一个任务的提醒时间只指定日期，但不指定时间，这个任务不会自动添加到日历的日程中，也不会有弹窗提醒。不提醒可以理解，但好歹应该加到日历中的日程中吧。同样的在IOS 13.12中，联系人的生日如果只选择日期，不选择年份不会添加到日历的日程中，在新版本中修复了这个问题。所以有理由相信这个设计问题被忽视了。这三个缺点看起来似乎没有ToDo的设计缺陷要重要，但是实际上对于体验上却又致命的关键。除此之外，两款软件的功能几乎一致。在多平台的速度上没有太大差别。界面细节“很”简陋对点击「已完成」/「未完成」没有震动的反馈，甚至在IOS上，已完成任务和未完成任务的样式是一模一样的，已完成的任务没有删除线，也没有文字变灰……很难受几乎为0的动画效果，非常不符合苹果的风格，很僵硬，甚至Mac的「提醒事项」双击任务有的时候会卡顿。子任务在任务编辑页面的最底下Mac上的显示方式还好一点，会将任务和子任务同时在列表中显示出来，子任务会有一个缩进。而在IOS的应用中，子任务的添加在任务编辑页面的最下方，你好歹也放在前面呀，子任务这个功能这么不重要吗……[album type="photos"][/album]最后最后决定，迁移使用苹果的「提醒事项」。我倾向使用内置的应用，一开始我甚至准备使用苹果的内置地图应用，但在国内比高德和百度差太远，就放弃了。华为的Android机器上用来记录的我一直使用的是便签而不是第三方应用。因为内置应用一般上更简单，更少的商业化。以上内容仅供参考~
我的选择Mac端：mweb 2.0移动端 ： 1writer这样我的跨平台写作流程是：在iCloud里面设置一个文件夹 写作文档。电脑端使用mweb的外部模式，打开该文件夹，进行写作。同时iCloud会进行同步。移动端使用1writer 打开外部文件夹，可以继续写作。[album type='photos'][/album]其他同类型应用使用体验一点吐槽首先统一吐槽一些软件，非得自己搞什么“文档库”，文档都不知道被软件存储到什么位置，然后必须使用软件内部的iCloud同步才能跨平台写作。最关键的是文件也不知道存储在iCloud的什么位置。我个人觉得写作软件：首先做好显示外部文件夹层次和文件就可以了。现在手机、电脑设备的文件系统都比较完善了。没必要搞什么应用内部的文档库，给我一种不安全感，因为我不知道文件存哪了其次应用内部也完全没必要搞iCloud同步，如果软件本身就支持打开iCloud外部文件夹，同步问题就自然解决了。而不是把用户的文档数据转成应用内部的数据，然后再用iCloud同步，这不是多此一举吗。评分标准满分100分。写作软件只要有以下优点，我个人就觉的是一款完美的软件：价格合理（20分）细节处理（比如主题切换、空格习惯设置、实时预览等等）（20分）支持对文件夹、标题和内容快速搜索，搜索结果呈现简单明了（10分）显示外部文件夹的层次结构以及相应的内容（20分）markdown解析流畅，对中文支持好，支持大部分的markdown语法（10分）对图片的上传有一个合理的方式（10分）至于能够同步博客、medium之类的功能对于我个人来说可有可无（5分）ui颜值不错，太丑的界面实在让人觉得 ...当然习惯了可能就好了吧（5分）除此之外的什么聚焦模式，打字机模式，有则优，没有其实根本也不影响写作体验。（0分）bear（移动端）（75分）[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明10元/月各种小功能考虑的比较好不支持支持内容、标题解析的样式好看，可以换主题 不支持同步博客之类的不是ios原生那种，但却是很精致得分1020010101005但是单就不支持外部文件夹对于我来说已经不会使用了。ia writer（移动端）（38分）因为我先用了ia writer，觉得差强人意，于是决定在移动端使用这款软件，没想到用户体验几乎为0。而且售价60，我不知道谁给他的勇气。[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明60元/买断自带markdown小键盘，有聚焦模式不支持支持搜索内容、标题，但搜索结果只有标题，没有匹配的内容差强人意，不支持公式如果是iCloud的文档，图片会自动放到iCloud里面，本地文档是无法上传图片的无不说了得分1015085500缺点：界面劝退，线条蓝色图标，我感觉像是上世界的Android风格，完全没有iOS的界面元素不支持外部文件夹打开，只支持外部某个文件打开，劝退1writer（移动端）（76分）[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务发布(5')UI界面(5')说明30元/买断有markdown工具条，还有一些个性化的设置我在iOS平台唯一看到的支持外部文件夹的软件只支持搜索标题差强人意会把图片自动放到相同文件夹类，无法自己设置无中等偏上，有改进的空间，虽然用了一些iOS设计风格，但整体上还是不够iOS得分20152055803ulysses（移动端）（45分）[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务发布(5')UI界面(5')说明28元/月，太贵了，iCloud一个月才6月呢这个软件打开你就能感受到花了很多功夫不支持支持内容、标题搜索，搜索界面也很好看不支持表格，样式还是很好看的没找到本地图片上传，只找到一个附件，但是和文章是分离开来的支持wp,ghost,mediumios原生界面，完美得分5200105055ia writer（电脑端）（66分）因为有试用期，我就下载了。基本功能还行，流畅性不够，尤其是右侧的编辑器区域，滚动起来就是不流畅，就是不流畅。而且聚焦模式用起来很……很不舒服。（可能我不习惯这个聚焦模式，内容始终显示在中间，上面还会有空白）语法高亮对中文也是没有作用的。字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明198元/买断差强人意，不支持更换主题颜色，写作的背景是灰色的，也不支持修改背景。灰色背景看得我很难受。支持支持，搜索速度很快没有代码高亮不支持从本地直接复制图片，也不支持调用第三方图床不支持差强人意得分51520108053Mweb（电脑端）（85分）[album][/album]这款是我在Mac端体验最好的，但是因为我刚买没多久就升级，而且需要重新购买，让我一直对这款软件有点意见！！我昨天升级了mweb3.0，有14天的体验期，我决定体验一段时间，如果没有优惠的话还是降级到mweb2.0，毕竟旧版本功能足够我使用，只是界面没有新版好看而已。支持多tab打开文档，这个功能非常好用，也是唯一一个我看到有这个功能的软件字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明128元/买断设置项实在太多了，切换主题都会小问题支持文档库和外部文件夹完美支持文字多了，滚动会卡（3.0专属bug），对表格的编辑支持也不够好支持第三方图床，也支持自动拷贝到文档文件夹下的media目录中支持多种，除了typecho基本都支持了我个人比较喜欢，相比ulysses还是差了一点得分1520201021053typora（电脑端）（95分）这是一款完全免费，也是很多人推荐过的软件，我很早以前一直使用，非常不错如果只是写markdown文档来说，这款软件非常棒，但在管理文件夹这方面是弱势，因为无法记忆上次打开的文件夹，每次启用都是一个新的空白文档。 现在已经支持打开设置一个固定文件夹了！！完美了但是整体设计，给我一种太轻巧的感觉以至于不太适合文件夹的展示，可能我没习惯。字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明免费对外观、markdown、图片、编辑器的设置基本都考虑到了支持支持用过所有软件中，解析最快，最流流畅的，完全是为markdown而生支持本地图片上传，不支持调用第三图床，但支持下载网络图片，支持ipic插件不支持简洁，美观得分20202010101005Ulysses （电脑端）（70分）也是在体验期下载，后面就没使用了界面好看，但是不支持实时预览，你如果想要看markdown的效果，得以pdf、text等某种文本导出才可以。[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明28/月有些细节有问题，比如 !> 会自动转化成 \!>,而且还没有开关，让人抓狂支持支持没有发现什么问题不支持从本地直接复制图片，也不支持调用第三方图床支持界面很好看得分515201010055tea (电脑端)（47分）[album][/album]完全免费，支持mac windows，移动端就没有了。这款软件我很早体验过一次，它自带的material风格（点击会有波纹效果）让我感觉和osx系统非常的不匹配。一种很不原生的性能很差的感觉。事实上，在文字编辑的渲染，总给我一种延迟的感觉。但是基于插件式的软件，很有创新点。有点类似vscode。但vscode更强大。这让我想起来，vscode本身也可以作为一个不错的markdown编辑器，但总给我一种杀鸡用牛刀的感觉。而且vscode也无法记住上一次打开的文件夹。还有一点vscode风格也是沿用Windows系统上面的。还有一个巨坑就是主打的所见即所得模式居然不能直接看到markdown的源码，必须导出为新的markdown文档，才能看到……我服了字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明免费还没简陋，我个人感觉不支持只支持当前文档搜索还可以无无还可以得分20100210005除此之外，我还尝试了ios上面的 markdown（够简洁，基本免费，但是不支持文件夹），pretext（更简洁，markdown预览不支持中文，不支持文件夹，基本就一个功能，编写markdown文件）总结中国开发者在markdown编辑器开发这方面已经越来越出色了。在界面、功能性上完全不输国外开发者。希望国内有一个和1writer一样设计思想的更优质产品出现。手机端，支持引入外部文件夹，我目前只发现了1writer。虽然bear、ulysses界面好看，但是贵啊，而且不支持外部文件夹。电脑端，基本都支持打开外部文件夹（除了 tea）。要免费，选择typora就好了，已经满足我的需求。或者也可以考虑mweb，在有优惠的时候。最不推荐购买的： ia writer 包括移动端和pc端，界面太丑。价格也不算便宜，至少在中文文档这方便稳定性、流畅性很差，不值。bear、ulysses 虽然订阅制，虽然很贵，但是购买之后不会觉得不值。从价格上，bear比ulysses更有性价比，两款软件功能很类似。ulysses界面我是最喜欢的，其次bear，次之mweb完~
当我从Linux转换开始使用macOS的时候，我发现macOS的终端很丑，它长这样：于是我找到这样一篇文章：Mac OS 终端 iTerm2 并配置 zsh。所以什么是终端，什么是zsh。终端很久很久以前，终端这个概念是这样的：是一种硬件，是一种输入输出设备，用于和计算机交换信息。终端的工作：用户通过终端输入命令，终端将命令传入计算机，执行后，并把结果输入到终端上显示。从这个概念不难可以看到电传打印机符合这个概念：但是现在计算机硬件一体化程度越来越高，硬件质量也越来越强，输入输出设备完全没必要单独用一个硬件和计算机进行交换信息了，取而代之使用软件终端和计算机进行交互信息。这就是终端模拟器 (Terminal Emulator)，也就是我们现在所讨论的终端。除了它不是硬件，它符合终端的概念和工作流程。举个例子，下面这些软件都是终端：1GNU/Linux：gnome-terminal、Konsole；macOS：Terminal.app、iTerm2；Windows：Win32 控制台、ConEmu 等。shell我之前很迷惑一个问题是，有终端就够了嘛，输入命令，执行并把结果输出，要shell干什么。更让人抓狂的shell还有很多种类，bash,cshell,zsh。这些到底是做什么的？所以需要明确下终端负责的工作。终端的工作范围：接收用户的输入，显示传过来的输出。（此外还提供一些优化用户体验的功能，比如支持文字复制粘贴、软件背景颜色的调整等）终端的工作其实是很少的哦，简单来说3点：GUI软件界面、接收输入、显示输出。而shell的核心工作就是操控计算机内核（如新建文件等等），即访问操作系统中的某个具有一定功能、可以处理具体事务的文件(二进制文件 ---- 此类文件经常被称作工具)、或包含一定内容的文件(文本文件)2shell分为：图形化 shell（图形化的桌面环境），如Windows Explorer （文件管理器）、 Linux的桌面环境有：KDE、GNOME、CDE、 XFCE等。命令行 shell。如bash 、sh 、csh 、ksh我们一般讨论shell都是指命令行shell。shell为什么叫做命令解释器不管是GUI shell 还是命令行 Shell，其实都是在解释命令，只不过GUI shell隐藏解释命令细节，用户通过点击鼠标的操作操作内核命令。shell为什么叫做壳如上图，shell通过解析命令调用 系统调用和相关API操作内核命令，进而读写硬件完成任务。shell是最外的一层的接收用户操作，像一层壳一样裹在整个调用过程中。为什么有这么多命令行shellshell与shell脚本语言并不完全相同的概念。shell除了包括对命令的语法规范（shell语言）和对命令解析，还包括一些额外的功能（如命令补全、主题等等）。shell语言和我们学习的任何其他高级语言c,python,java没有太大区别。不同的shell语言可以看做java语言的不同版本。不同shell的区别主要是两方面：脚本语言语法有差异功能、外观、自定义美化上有差异脚本语言早期的sh甚至不支持给长命令设置别名的语法。3zsh虽然完全兼容bash的语法，但还有一些语法差异，具体可以看Zsh和Bash，究竟有何不同 坑很深自定义功能配置除非我们需要编写shell脚本，普通用户一般很难发现不同shell脚本语言差异。对文件操作ls、pwd等等这些常用命令所有shell都一样支持。所以我们普通用户更关注是细节、外观、功能上的差距。这一点zsh做的格外的优秀。比如色彩高亮、命令提示、智能补全、快速跳转等等附加功能。4我们在讨论配置终端的时候，实际上是在讨论配置shell。为什么zsh如此受欢迎？这个较新的shell与bash兼容，但包括更多的功能。 zsh shell提供内置的拼写校正，改进的命令行完成，充当shell插件的可加载模块，允许您在命令行上别名文件名或其他任何东西的全局别名，而不仅仅是命令，以及更多主题支持。 它像bash，但有很多附加功能，附加功能和可配置选项，你可能会喜欢，如果你在命令行上花费大量的时间。3总结： 终端就是软件，是最外层的，shell = shell脚本语言解析器+编辑、外观等额外功能不同shell的切换输入cat /etc/shells，可以查看本机上的所有shell：临时切换比如切换bash，直接输入bash，即可切换到bash环境下，输入exit，复原默认shell环境。默认切换5#使用系统自带的 zsh：输入密码成功切换，重启终端即可使用 zsh。 chsh -s /bin/zsh #使用第三方的 zsh，如从 brew 安装的 zsh。 sudo chsh -s /usr/local/bin/zsh 命令行界面 (CLI)、终端 (Terminal)、Shell、TTY，傻傻分不清楚？ ↩ Shell 是用来解决什么问题的？ - Bryan Yuan的回答 - 知乎 ↩ Bash，Zsh和其他Linux Shell之间有什么区别？ ↩ 为什么说 zsh 是 shell 中的极品？ - 韦易笑的回答 - 知乎 ↩ 终端中的 zsh 和 bash ↩
[scode type="red" size="simple"] （网络计算研究院2020年被合入了计算机学院）关于网研1组的名额的事情我是完全不清楚，抱歉[/scode]本来这篇文章是很早之前写的，写到一半写不下去。现在上岸了，我打算完结这篇文章。可能是写的心情不一样吧。知乎上初试学习方法帖子很多，我的初试成绩也不高，所以我只打算一些我曾经迷惑的问题。报考：北京邮电大学 网络技术研究院 计算机科学与技术 {人工智能、网络智能化、人机交互}研究方向笔记集合[post cid="916" size="small" /]初试总分： 342(101)思想政治理论：58(201)英语一：66(301)数学一：100(803)计算机学科基础综合：118我希望我的一些文字能让你更轻松的面对考研。当我回过头来看，我们作为大学四年的成年人，我们不应该再和高三一样，把考研当做人生的一道坎，或者当做一个特殊而重大的任务。我们应该把考研当做生命的一种经历，一个过程，一个体验。就像我之前说的，我觉得人生不应该是工作、放假、工作、放假这样的循环。不管是很累的工作、还是放松下来的假期，我们都应该坦然的去经历。去年这个时候我还待在家里，考研我一无所知，我不知道怎么报名，我当然还幻想可能可以保研，也不知道考研只能报一首学校，以及如果学习准备我也一无所知。所以我想说的是2020年考研的小伙伴完全来的及，先不必急。我当时只是心里有点着急，但实际上什么也没做。不知道考研是自己看书还是听视频。正式备考（7月份）之前，也随大流的一样的每天拿的数学训练册做几题玩一会手机，又或是看看课本，根本都不做题。到了8月份时候，我发现我之前看的高数，线代，概率论，我彻底彻底忘了，我看到一些概念，我甚至不知道什么意思。因为我没做题。我回答一下可能疑惑的一些问题，这也是我当年困惑的问题：考研累吗累，但是别无选择。我是7月份开始备考的，当时夏天天气最炎热的时候。一开始学习效率不高，第一个月还偶尔去看电影。不是很紧张。学习过程中看手机。但是我很快调整过来的，因为时间太紧了。我觉得可以从这几个方面缓解：首先，自己为什么考研？我自己考研首先我的专业课知识学的太不扎实，考研前，我的计网一塌糊涂，数据链路层我没理解这个链路到底什么意思？一条路吗？所以我没办法找工作。其次，我希望我在研究生中能学到更深层次的理论知识，如果一旦工作，基本很少有学习时间和自己的空闲时间的，我觉得我没办法很快接受这样的环境。其次，给自己复习订一个整体时间规划：我当时规划是这样，7-8月看完数学课本，7-9两个月看完4门专业课。10-12月每个月大概完成什么任务。每天也要有每天需要完成的计划，我一开始总是每天计划内容太多，导致完成不了，所以计划一定要合理。计划需要定期根据你的进度调整，这样你心里有数，你的进度到哪了。ps：也不至于网上说的不洗头，没必要，该整理自己还是要整理的干干净净的，头要洗，不然头发都没了每天学多长时间我每天早上9点醒，10点才开始起床学习。10-11：50 上午学习13：00 - 17：50 下午19：00 - 22：00 晚上这样一天也有10个小时了，到了10月份之后，晚上学到22：30左右。对于我来说，可以了。我早上起的很晚，大家可以根据自己作息安排，保证一天任务量能够完成即可。我也尝试过早起7点或者8点。试了几天，发现上午特别困，都快睡着了，根本学不进去，就索性放弃了。学习时候带手机吗不带。我一开始带。到了10月份之后就彻底不带，因为时间彻底不够了。有人说，查单词啊，要用手机……我觉得没必要，张剑的英语黄皮书，赠送了一个词汇本，遇到单词用这个查并标记好就行了，上面查不到的单词就没必要管。当然个人有不同安排，但是我建议不带。因为真的学习过程中很枯燥的，尤其是前期，不是太紧张的话很容易松懈。跨考计算机难度大吗我今天问了室友才知道三跨是什么意思。跨专业、跨省、跨学校。其实难度最大的应该是跨专业。跨专业的同学认为自己底子差，和计算机专业同学没办法竞争。老实说，本科学校交给我们的实在是太少太少，而且本科上课不积极，不认真。计算机网络、组成原理本科学的时候我也没有学明白，只是为了应付考试，做了一些题目，获得不错的分数。这些专业课知识都是我在考研期间才学的更仔细了一些。我是学渣，我摊牌了本科上课学的语言C / C++/ Java，其实都是学的很浅。C的语言我忘了差不多了，C++ 没仔细学，主要用的最多是java。语言你学了一门，你会发现语言是很有基础语法是差不多，所以跨考的同学熟练掌握一门语言我认为可以了，在这个基础上，再了解别的语言不会花费很多时间。(我个人认为java是门很容易基本使用的语言，因为很多方法都是jdk提供的，比如动态数组、list，当然c++也有，我更喜欢java)所以跨考学生不必要担心和别的同学能力差很多，计算机综合专业知识可以在初试学习阶段的时候弥补。开发语言知识可以在初试结束后弥补。什么时候开始正式复习有的人从大三开始准备，有的人今年3月份开始准备。我是暑假7月份正式开始的。说到底需要自己制定一个整体的时间计划。我再3月份时候也打算准备，但是周围根本没那个气氛，我也认识不到考研的严峻性和重要性，甚至对考研一无所知，所以这个时候准备是被动。如果再考研一次，我希望我能提前准备，不要后面慌慌张张的。把学习、准备这件事情当做生活的一件普通小事，细水长流。我知道说起来很简单，做起来真的很难。专业课怎么准备专业课 118，不高，但是我现在没弄明白为什么这么低。因为除了选择题有几道题常识性问题我不确定，大题数据结构我写的代码可能不是很完美，其他专业课的题目我都会，计算也验算了，不知道是不是算错了……考研前，我的专业课知识绝不比任何人好，虽然我的成绩单写的分数都是90、80分，但是那些都是考试前熬了几天夜，然后做了试卷得来的。对每门课里面有很多一知半解的问题。我的方法就是做笔记。一门课一门课的学，一章一章的学。比如计算机网络，每一章，我会把我搞不明白的问题都写下来，然后一个个一些搞懂，并写到纸上。这轮专业课复习不是面向试题的，只是纯粹的想学懂专业课知识。最后，我4门课，每门都有大概40页A4纸的笔记。我买的天勤系列，除了数据结构，其他的简直是垃圾。垃圾课本，误人子弟。计算机网络，买谢希仁第七版，讲的真的好！数据结构看天勤的差不多了，我还参考看了李春葆编的那本数据结构。计算机组成原理和操作系统用的就是大学的教材，这个应该都差不多。第二轮专业课复习，就是做题。我建议两轮可以交叉来。我第一轮完成后，开始做题发现好多知识又忘了……做题才是加深记忆的好方法，考研考的就是记忆，重复，做题。要不要看视频、报班？除了政治马原、毛中特的视频可以选徐涛的视频，或者“腿姐”（我听的是徐涛，马原里的资本理论讲的很容易理解），数学我觉得没什么必要看视频，自己多总结，别人就是告诉你题型以及解法。你遇到题目仍然不会做。要和别人组队学习吗？建议找一个小伙伴，每天吃饭时候一起去。没小伙伴，你每天吃饭可能很不规律，甚至时不时给自己放个假。我一开始自己一个人学，然后比如中午就有时候学的时间就会推后，导致下午学习就乱了。但是不一定要在一起学，可以是不同教室。我和我室友就是这样，吃饭点在楼底集合。复试北邮总成绩（满分100） = 初试成绩（总成绩500） /10 + 复试成绩（总成绩100） /2表头分别是，姓名、专业、研究方向编号、英语面试成绩（满分10分）、笔试成绩（满分30）、面试成绩（满分60）、复试总成绩（满分100）、初试总成绩（满分500）、考研总成绩（满分100）最终总成绩排名第6因为我是最后一名进的复试（一共11个人），当时真的很慌。机试今年网研院机试很简单，不能带纸质资料进考场了，以前(2018年及其以前)可以。（用大佬们的话说就是人均4A）47分钟ac了4题。OJ排行榜第八，但是我其实没有acm经历，只是靠的速度而已，嘿嘿嘿。第一题，输入四个数，输出最小的第二题给定年份范围，判断闰年个数第三题，链表操作，按值查找，删除，反序链表 这个我要吹爆Java的list，API好用到爆第四题，求图最小生成树，我一开始以为我做不出来，因为我没看这个代码，但是幸亏我看了思路，不是很难今年题目测试用例都很简单，没有特别奇怪的输入要考虑。本地通过样例，直接提交就过了。说下我觉得小技巧吧：提前大概30分钟进考场，随便选一台机器。可以选把编译器打开，每题第一行都是输入测试数据组数，先把这些代码写好。然后左右分屏，网页在左边，编译器在右边，边看边写很快。支持语言 c/c++(支持stl)/java 8。现在支持java 8 了，可以看下java 8 的新特性，有的还是很有帮助的。(我一开始以为只支持java 7，所以没看java 8 新特性)机试训练可以借下学长的VPN网关账号，去刷下北邮的往年机试题目。关于往年的机试题，可以看下这里 [post cid="916" size="small" /]笔试官网写的是占复试成绩30%，但是不及格不影响录取。但是会影响录取后的排名，最后的排名是按照初试成绩/10 + 复试成绩/2。第一年发奖学金是按照这个排名的(一等奖8000，覆盖70%，二等奖4000，覆盖30%)笔试全部是选择、判断题，主要考察对概念的认识理解，不是很深入。我掉的坑就是我之前看的2015年的计算机系统结构题目是计算题，我以为考的是流水线计算题，结果都是概念题，而我没好好准备！！笔试基本都是概念题，计算题好像是没有！关于笔试的笔记也可以参照这篇文章 [post cid="916" size="small" /]面试我没有项目怎么办我觉得项目不重要，而是看你的计算机能力怎么样，这个能力包括对新技能的学习能力、已掌握的计算机方面能力。比如，面试的人工智能专业，在初试后准备Python学习和相关算法学习，这个就可以写在简历上。如果时间来得及，可以自己做一个感兴趣，但是规模不大的项目，比如对自己家庭的人脸识别(这个其实调用现成库就可以，主要是对整个流程和算法思想有所了解)，也可以写在简历上。非跨考的，平时的课设一定有不错的，也可以写在简历上。项目不多，老师可能多问一些专业课知识，这方面就要多准备一些。北邮网研面试全纪录[post cid="922" size="small" /]北邮报考的一些问题北邮报考难吗知乎上都说，有的组难度甚至和985齐平，我觉得这么说不准确。和普通一点的985可能齐平，但是和计算机学科排名靠前的比如北航，还是要容易一些。大家可以参考今年北邮的组线和别的大学的院线（复试线）就可以知道情况了。我报考的组组线是342，我是最后一名进入复试的。别的组除了网研2组专硕350+，其他组的组线都不是很高。计算机学院的组线平均在340+左右。所以打算包括北邮的，应该将初试目标分数定在360+，这样进复试比较稳。当然进复试之后还得继续准备。（我们组就有370被刷掉了）计算机学院的组线还是比较稳定的，340+，但是其他学院比如网研、网安、软件有的组分数很低。我看到一个278上的网研4组专硕什么是报考到组这个问题我一开始愣是没明白，其实没这么复杂。报考的时候我们都要选择某个学院——某个专业——某个研究方向(组)只不过一般的学习是整个专业一起面试，所以一起复试的人很多。而且你被录取了，你以后的研究方向不一定是你报考的那个方向，只是作为一个参考。北邮复试，是按研究方向一起复试的。你填什么研究方向，录取的就是什么研究方向。所以进复试的人相对比较少。研究方向又叫做组。比如我报是 {人工智能、网络智能化、人机交互}，组号是1，组号就是研究方向编号。北邮还有一个名词是中心。就相当于院下面还有一个子院。这个子院负责几个专业的几个组。比如我们网络研究中心，负责计算机科学与技术（学硕）的1组(编号为1的研究方向)和计算机技术(专硕)的14组(编号为14的研究方向)以及信息与通信工程（专硕）的1组，和电子与通信工程（专硕）的1组。中心本身又分为几个工作组。被录取后会被分配到哪个工作组下面学习工作。总结：报考是报考到某学院下面的某中心的某组，复试名单以及面试按该组进行面试，录取后会被分配到该中心的某个工作组。要提前联系导师吗我中心是“导师组”制，一直是统一招生，统一管理的，和很多中心的“导师制“不同，具体是哪位导师的意义没有那么大。大家不需要事先联系导师这个是王老师原话，我其实没看懂……我出了初试成绩，家里人催着我给老师发邮件，联系。甚至要马上去上门拜访，被我理智制止了。其他中心似乎需要联系导师，有考试北邮学长说，如果可以考研前联系好，就可以直接去实验室工作了……这方面我不太了解调剂是怎么回事，怎么操作第一次调剂是出了复试名单之后，你没进复试，可以进行调剂。调剂最好先联系好导师。第二次调剂是复试被刷了，校内会有二次调剂机会。有一个学长写一个github就是关于北邮录取的，更多信息可以看这里：See_you_in_BUPT北邮歧视双非吗根据往届学长学姐们所说，北邮还是很公平的。我自己认为，很多双非的初试分数很高，但是复试还是被刷，原因有可能不是歧视双非，而是你除了考试的专业课知识之外知识掌握的不够。换句话说，大部分双非的考研期间可能很努力，分数很高，但在大学其他时间内努力不够，导致的专业素养不够。再换句话说，只要你有实力（包括考试成绩和其他专业技能），北邮考研还是相对公平的选专硕还是学硕？（此答案是北邮研一经历一学期之后写的）专硕和学硕在培养方案有一定区别，首先学硕可能在学校实验室这边搞科研，也可能在公司那边上班实习做项目之类的，专硕基本都会被分配到公司那边做项目。专硕毕业难度低，不需要发小论文，毕业要求低。学硕毕业必须发小论文，而且老师可能要求很高。很容易被怼，如果没有创新点的话。专硕研究生课程的要求也会低一点，比如数学类的课程只要求必选一门，总学分要求也低一点。专硕和学硕在社会的承认度这点我不能很确认，至少就我个人来说，两者应该差距不是很大。总的来说，专硕的要求低，而且从近几年来说，有些组录取分数和学硕很接近，有的组，专硕的分数还是很低的。两者都是3年制。致谢考研经历时间太长了。感谢考研期间一直一起吃饭的一起聊天、吐槽的室友@林国杰(他复试还没完成，祝愿他能上岸)感谢万静老师一直以来的关心和帮助，感谢所有学长学姐提供的经验，还有复试群里面提供的资料！也感谢我的父母在我考研期间没有给我过多的压力！最后考研过程中，一定会有不想学习的时候，一定有痛苦怀疑的时候，一定有焦躁不安的时候。经验帖子看的再多，也都是别人的故事，只有自己走过来了，才觉得这段时间自己没有辜负，自己除了知识方面，在思想方面也一定有所成长。即使最差的结果失败了，这段时间也一定不是白白浪费的。当经历过来再回过来来看，也不过是一次考试的准备而已。人生路上的选择、挫折、困难还很多。希望我的经历能对你有所帮助~如果有别的问题可以在评论区留言哈~如果我知道的，我会尽量回答的
!> 总结：QC 35 头戴式耳机个人不建议买，原因：头戴式，时间带长了，头发压得很难受。而且戴眼镜的话，降噪效果会差一点。QC20 降噪神器，十分推荐，买过这么多东西，便宜的，贵的。QC20 是我买的最值得的一件物品。（因为我特别讨厌噪声，特别讨厌）我个人是对噪声很敏感以及很讨厌的，所以自从入手了降噪耳机，可以说生活烦恼少了很多。Bose qc系列都是降噪耳机，因为很早以前入手的降噪神器qc20（入耳式），最近又入手了qc35II（头戴式），所以就我的体验写下此文，做下对比，给想要入手的小伙伴做一个参考。先上两款的实体图：（图来源见水印，我自己拍的不好看就不拍了 ::twemoji:sweat:: ）[album type="photos"][/album]降噪效果首先两款耳机都不是全频降噪的，不可能戴上后什么噪声都听不见的，对环境噪声的消除比较干净，对人声和机械键盘声有减弱的效果，但不能完全消除，对广播声音、大声说话声也是不能完全消除的qc20消除人声的效果要比qc35ii效果好，原因之一可能是qc20开启降噪后有白噪声的底噪（事实上，我个人觉得这个底噪并不会觉得烦躁，开启降噪后再听歌根本听不见底噪，而且这个底噪可以更好隔绝人声），QC35II戴上后，基本没有任何底噪，如果在安静的环境下，你会怀疑是不是开了降噪。佩戴舒适程度虽然说qc20的耳塞是鲨鱼鳍设计，而且购买时候会有大中小三款耳塞可以调换，但是我平时工作时候连续带3小时听歌的话，耳朵还是有点酸痛的（但是相比忍受噪声，这根本不算什么好嘛）。qc25II头戴式，戴起来还是很舒适的，连续带几个小时，耳朵有点压力，不过比qc20要好点。而且如果戴眼镜的用户，虽然戴起来没什么毛病，但是降噪效果会变弱（因为眼镜框会让耳套有缝隙）。续航方面qc20官方数据是连续降噪16小时，qc35II无线连接连续高降噪是20小时，我觉得qc20的续航真的很好，能用很长时间。qc35II还没有充电过价格方面京东自营还是比较贵，可以选择京东全球购，但是要确定是正品，我的qc20是1600多，qc35II是2050关于爆音qc20确实有爆音，在地铁上不会，在公交车比较颠簸的话会。因为我之前一直坐地铁才带降噪，公交时间比较短一般不戴，后来才发现这个问题。不过还能忍受吧。严重的话每隔10s大概会爆音一次。不颠簸应该没问题。其他方面如果单纯受不了噪音，而且是说话声，键盘声，强烈建议是qc20，降噪神器！！就算不能完全消除人声，你把耳塞塞的紧一点，效果都会好一点，而且戴眼镜也不会影响降噪效果。如果想买无线的，那只能选择qc35II了，事实上还有qc30（耳塞式），不过续航能力差点，而且设计俗称“狗圈”，百度下就能知道了，qc35第一代和第二代几乎没有差别，就是二代多了一个降噪等级开关（没卵用，我一直是高降噪都嫌不够用了，怎么可能开低降噪）
eclipse简直反人类！！！反人类！！！服务器开了几个请求，就自动闪退...然后就下定决心转到IDEA，下面是一些我遇到的坑。前提我的项目是用maven管理的，也就是项目根目录有一个pom.xml 管理插件的文件。导入过程导入项目转移的时候，直接使用IDEAimport project,再选择maven，就能够初步导入成功然后一路next就可以了。配置项目在顶栏的Edit Configurations添加一个配置：一些错误的解决最可能出现的错误列表如下：Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'spotDao' defined in file （找不到dao的映射xml文件，原因是IDEA没有找到你的source目录）IOException parsing XML document from class path resource [spring-mybatis.xml];（找不到spring-mybatis.xml，IDEA没有找不到你的配置文件的目录）java.lang.IllegalStateException: BeanFactory not initialized or already closed - call ‘refresh’ before accessing beans via the ApplicationContext（应该是bean无法加载到，没有配置好source目录）而且最为奇怪的是，虽然你没配置好，有时候不出问题，有时候又莫名其妙的坏了。所以一定要配置好pom.xml文件这些错误的原因都在于，使用maven直接导入到IDEA的时候，配置文件IDEA找不到，不知道为什么，eclipse就能够找到在pom.xml中的build标签里面增加： <build> <finalName>TourServer</finalName> <sourceDirectory>src/main/java</sourceDirectory> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> </build>配置一下sourceDirectory和resource就可以了。一切解决，使用IDEA更高效开发吧~
系列笔记[基本数据结构]()[类的使用]()[类的继承和派生]()[面向对象特征之一——多态]()[操作符重载]()[const关键字的使用]()1. 类的定义与使用1.1 类的定义最简单的类的申明：class Student{ };在类的定义最后需要加上分号，这是和java不同的地方，否则会有编译错误。1.2 类的使用即创建类的实例——对象。在java中你会使用到new关键字，而在c++中，创建对象与使用普通的数据类型一样简单：Student studentObject;2. 类的成员变量和成员函数2.1 成员变量class Student { char name[20]; //姓名 int id_num; //学号 int age; //年龄 char sex; //性别和java不同，类的成员变量不能在创建的时候初始化值。2.2 成员函数和java不同,c++的成员函数有两种定义方式:1.类内部申明，类外定义（实现）：class student { void set_age(int a); int get_age(); }; //在类外部定义set_age函数 void student::set_age(int a) { age = a; } //在类外部定义get_age函数 int student::get_age() { return age; }在类内部申明函数，在类的外部定义的时候通过作用域操作符 :: 进行实现。申明的时候，只需写出返回类型、函数名称、参数列表、末尾分号即可，不能加上空的大括号。 返回类型 类名::成员函数名(参数列表) { //函数体 }你也可以在类的成员函数申明的前面加上inline关键字使其变成内联函数。内联函数可以优化程序的执行效率。class student { inline void set_age(int a); inline int get_age(); }; //在类外部定义set_age函数 void student::set_age(int a) { age = a; } //在类外部定义get_age函数 int student::get_age() { return age; }2. 类内部边申明边定义（实现）class Student{ void set_age(int a){age = a;} int get_age(){return age;} }这种方式其实和java的成员函数调用方式是相同的。2.3 访问限制和java的语法类似，c++也提供了三种关键字：public：本类和其他类都可以访问到private： 只有本类可以访问到protected：只有本类和派生的类可以访问。但是使用的语法格式有点不同。class book { public: void setprice(double a); double getprice(); private: double price; };通过这种格式，可以将属性为public的变量或函数申明在一起。避免了重复的写权限限制符号。当然，权限相同的成员也可以分开声明，申明的顺序也是无所谓的。2.4 访问成员变量/函数创建的对象就是类的实例，就直接使用.点选择符，这里是和java一样的。创建的如果是对象的指针，需要使用->箭头选择符访问成员。java中没有指针就没有这条规则。#include <iostream> using namespace std; class book { public: void setprice(double a); double getprice(); private: double price; }; void book::setprice(double a) { price = a; } double book::getprice() { return price; } int main() { book Alice; Alice.setprice(29.9); cout<<"The price of Alice is $"<<Alice.getprice()<<endl; book *Harry = new book; Harry->setprice(49.9); cout<<"The price of Harry is $"<<Harry->getprice()<<endl; return 0; }注意：C++类class和结构体struct区别class和struct都可以定义一个类的结构。采用struct关键字，结构体中定义的成员变量或成员函数默认都是public属性的。使用class关键字，类中定义的成员变量或成员函数默认都是private属性的，除此之外，没有其他任何区别。3. 构造函数3.1 构造函数的定义构造函数的声明与定义的方式与普通函数是相同的，也是有两种方式，但是有以下几点特殊地方：构造函数的函数名必须与类名相同；构造函数无返回值；当我们创建类对象的时候构造函数会被自动调用，而无需我们主动调用。这三点和java中语法也是一样的。通常如果在定义类的时候，没有定义任何一个构造函数的时候，系统会自动生成一个默认构造函数。默认构造函数就是不带任何参数的构造函数。其它带参数的构造函数统称为带参构造函数。如果在类中声明了任何一个构造函数，则系统不会自动生成默认构造函数。如果在创建对象时候表示使用哪个构造函数呢？只要在对象名称后加一对小括号，小括号类参数与对应的构造函数参数对应即可： class book { public: book(){} book(char* a, double p); private: double price; char[] title; }; book::book(char[] a, double p) { title = a; price = p; } int main() { book Harry("Harry Potter", 49.9); return 0; } 3.2 初始化参数表除了通过在构造函数的函数体内进行成员变量的初始化，还可以通过在构造函数的后面以下面的格式初始化参数：构造函数(参数列表):成员变量1(初始值),成员变量2(初始值)……{}举个例子：class book { public: book(){} book(char *a, double p):title(a),price(p){} };对，你没有看错，还有这种类似函数的方式给成员变量赋值。这种写法本质就是为了简写成员变量的赋值操作，你同样可以用普通的方式定义构造函数： book(char *a, double p){ title = a; price = p; }在java语法中，你可以在初始化块中给成员变量中赋值，很容易理解。但是在c++中提供这样一种以成员变量名作为函数名，参数列表是变量的值的形式初始化成员变量。值得注意的是：参数初始化顺序与初始化表列出表量的顺序无关，参数初始化顺序只与成员变量在类中声明的顺序有关。3.3 构造函数的参数默认值PHP中的函数参数可以在调用之前就可以赋值一个默认值，因为PHP不支持函数的重载。通过这种方式，当你调用的函数时候没有给其他参数赋值，其他函数也会有一个默认值:function exampleFunction($parm1,$parm2 = "world"){ echo $parm1 ." ". $parm2; } exampleFunction("hello") //输出： hello world;上面的代码可以很清楚的看到，第二个参数已经被赋值了默认值world，当你没有给定第二个参数的时候，第二个参数仍然是有值的。c++的构造函数也支持参数默认赋值，比如：class book { public: book(){} book(char* a, double p = 5.0); private: double price; char * title; }; book::book(char* a, double p) //在定义函数的时候可以不指定默认参数 { title = a; price = p; }此时，当我们这样创建对象的时候：Book Time("Time book")调用的就是第二个构造函数，并且price自动赋值为默认值5.0但默认参数的构造函数会与某些构造函数重载冲突：book(char* a);如果我们再申明这样一个构造函数，之前创建对象就会发生编译错误，因为编译器不知道调用哪一个构造函数了。换句话说，C++在支持构造函数的重载的条件下，基本上没必要使用带默认参数的构造函数。* 3.4 转型、拷贝构造函数3.4.1 转型构造函数构造函数关于是否有参数可以分为：不带参数构造函数带参数构造函数（含带默认参数的构造函数）其中带参数构造函数中有两种特殊的构造函数：转型构造函数拷贝构造函数转型构造函数和普通的构造函数没啥区别，就是参数列表只有一个参数，创建对象的时候如果调用该构造函数，就是意味着将参数转换为了一个类的对象，这没什么特别的。特别之处在于这里有一个隐式的类型转换：比如你创建一个字符串变量，但是你将该变量传入了一个参数为类对象的函数，这个时候就会自动的发生字符串到类对象的隐式对象类型转换：class student { public: student(char * n){name = n;}//转型构造函数 private : char * name; } void fun(Student studentObj){}; char * name = “Harry Potter”; fun(name); 这个时候字符串name自动转换为student类的对象，转换的过程中自动调用转型构造函数。但隐式转换会带来调试的困难，可以在构造函数前加上explict关键字，避免这种自动转换。换句话说，转型构造函数就是参数只有一个的一类构造函数，能实现将参数转换为类对象的功能。3.4.3 拷贝构造函数4. 析构函数在Java中无需自行设计析构函数，java有一套系统级的垃圾回收管理程序。在创建对象的时候系统会自动调用构造函数，在对象需要被销毁的时候，系统也会自动调用析构函数。与构造函数类似，析构函数也是一个成员函数，但是有如下特殊之处：无返回值没有参数，不能被重载，因此一个类也只能含有一个析构函数函数名必须为“~类名”的形式，符号“~”与类名之间可以有空格5. 类的高级用法5.1 常量指针this与java类似，在类的每个成员函数中包含一个常量指针，该指针指向调用该函数的对象。与java类似，该指针只要是用来区分同名的成员变量与参数名称的：void setprice(double price) { price = price; }5.2 new和delete操作符c++创建对象有两种方式，一种就是普通的直接通过类名 对象名(构造函数参数列表) 创建，另一种就是通过new关键字创建。两种方式创建对象的空间分配方式有分别。第一种空间分配在栈中，由系统可以直接管理，比如在代码块外，块内的对象就会被清理。第二种的空间分配在堆上，大小可以很大， 但是销毁得由程序本身处理。使用 new 关键字会自动调用构造函数使用 delete 关键字会自动调用析构函数5.3 const关键字const表示一些不希望被人们修改的数据，与java语法中的final static 有类似之处，但是也有很多区别。const关键字可以定义在成员变量、成员函、对象、对象的常引用上。5.3.1 修饰（成员）变量这个很好理解，在变量最前面加上关键字表示初始化之后禁止二次修改。const成员变量的初始化只能通过参数初始化表进行初始化 const int a=5;5.3.2 修饰成员函数const成员函数可以使用类中的所有成员变量，但是不能修改任何成员变量，也不能调用类中任何非const成员函数。fun(形参) const{}5.3.3 修饰类对象用const修饰的类对象，该对象内的任何成员变量都不能被修改。因此不能调用该对象的任何非const成员函数，因为对非const成员函数的调用会有修改成员变量的企图。class A { public: void funcA() {} void funcB() const {} }; int main { const A a; a.funcB(); // 可以 a.funcA(); // 错误 const A* b = new A(); b->funcB(); // 可以 b->funcA(); // 错误 }5.3.4 修饰类对象的引用我们在有些函数参数中会使用到对象，这个时候我们不是直接将对象做为参数，而是使用对象的引用。此时我们在对象的引用前加上关键字，避免函数对对象本身做出任何修改：void display(const book &b) { b.setprice(59.9); //compile error cout<<"The price of "<<b.gettitle()<<" is $"<<b.getprice()<<endl; //ok }5.3.5 重载成员函数参考文章： C++ const关键字总结5.4 static关键字好了，这个关键字可以说是和java一致的了。5.4.1 静态成员变量该变量是同步于类的所有对象，也就是类的变量5.4.2 静态成员函数5.5 友元函数和友元类5.5.1 友元函数我们知道对象是无法直接调用私有变量或者私有函数的，只有在类中的函数才可以使用类的私有变量或者私有函数。友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数，它不属于任何类，但需要在类的定义中加以声明，声明时只需在友元的名称前加上关键字friend，其格式如下：friend 返回类型 函数名(形式参数);友元函数的声明可以放在类的私有部分，也可以放在公有部分，它们是没有区别的，都说明是该类的一个友元函数。一个函数可以是多个类的友元函数，只需要在各个类中分别声明。友元函数的调用与一般函数的调用方式和原理一致。5.5.2 友元类友元类的所有成员函数都是另一个类的友元函数，都可以访问另一个类中的隐藏信息（包括私有成员和保护成员）。 当希望一个类可以存取另一个类的私有成员时，可以将该类声明为另一类的友元类。定义友元类的语句格式如下：friend class 类名;当你在一个类中申明另一个类为该类的友元的时候，表明该友元类的所有成员函数都可以使用该类的所有信息。这种顺序是单向的。其中：friend和class是关键字，类名必须是程序中的一个已定义过的类。例如，以下语句说明类B是类A的友元类： class A { … public: friend class B; … };经过以上说明后，类B的所有成员函数都是类A的友元函数，能存取类A的私有成员和保护成员。使用友元类时注意：友元关系不能被继承。友元关系是单向的，不具有交换性。若类B是类A的友元，类A不一定是类B的友元，要看在类中是否有相应的声明。友元关系不具有传递性。若类B是类A的友元，类C是B的友元，类C不一定是类A的友元，同样要看类中是否有相应的申明参考文章： C++友元函数和友元类
前言之前学校开的选修课《数据挖掘》，布置的两道算法题，时间有限完成其中一道：用Apriori算法求特定支持度的频繁项集。算法本身不难，java萌新我却花费了一天的时间，特此记录。算法描述我们目的是求出项数为K的频繁项集即L(K)。Apriori算法的核心步骤是：L(K-1)通过自连接求出项数为K的候选项集合C(K)通过对C(K)进行一系列处理（剪枝 + 支持度判断） 得到L(K)集合在说明下面更多内容之前，先对一部分概念进行说明：事务：如第一张图中，每一行就是一个事务，如 T100 I1,I2,I5。其中T100是事务编号，这个随便命名，只要保证唯一即可。后面的I1 I2 I5是该事务中的项数据集：如图一，整个表就是数据集，就是所有的事务的集合。即D = {T100,T200…}项集：包含若干个项（如I1 、I2这样的项）的集合。可以看出项集和事务似乎有点项。但是项集更随意一点，任意几个项都能组成一个项集。但是事务是人为规定好的。可信度：项集在数据集中出现的重复次数。项集不一定需要在事务中连续出现。如{I1,I2,I5}项集在数据集中出现的次数为2次，分别是第一个和倒数第二个事务。接下来，详细分析算法中的两个步骤：1. 自连接：怎么个连接法呢？以图二中的L2频繁集的集合为例：项集支持度{I1,I2}4{I1,I3}4{I1,I5}2{I2,I3}4{I2,I4}2{I2,I5}2判断两个项集是否可以自连接要看两个项集的K-1项是否完全相同。如果满足条件，连接后的项集 = 第一个项集 + 第二个项集的最后一个元素。比如{I1 I2}与{I1 I3}满足自连接条件，连接后的项集为{I1 I2 I3}。2. 剪枝：剪枝是这个算法的核心，如果不进行这个步骤的话，也能得出正确结果，但是时间就会大大增加了。剪枝的核心是若某个集合存在一个非空子集不是频繁项集，则该集合不是频繁项集。我们通过自连接组成新的K项的候选项集后，需要通过剪枝判断是否满足条件。即找出该候选项集的含有(K-1)项的子集，并分别判断每个子集是否存在于K-1项频繁项集里。只要有一个不存在，那么该K项候选项集也不可能是频繁项集。这里我们只需要找含有K-1项的子集，而不用找所有的子集，因为这个算法是从下不断递归上来的，含有更少项的子集肯定是在频繁子集里的。问题关键是怎么寻找某个K项集的K-1项子集呢？很简单，依次把K项集中的每个元素去掉一个就可以了。完成了这两步骤，就完成了这个算法了核心步骤。3. 对精简后候选集统计每一个项集的支持度4. 根据最小支持度将候选项集转换为K项频繁项集合可以看出，如果不剪枝的话，第三步的工作量是非常大的。剪枝过后，候选集的体积大大减小了。但是获取含有一项的频繁项集因为没有候选项集，就需要特殊处理。这个很简单，就是统计数据集中的每一项的支持度，并和最小支持度进行比较，得到含有一项的频繁项集。代码分析github项目地址：https://github.com/ihewro/Aprioriimport java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.*; /** * @author hw * 算法名称：数据挖掘：Apriori算法求特性支持度下的所有的频繁集 * 算法原理： * * 0. 获取C1的时候，因为没有L(0)频繁集，所以单独处理一个函数 * 1. 已经频繁集L(k-1)，{}自连接+剪枝}求出精简后的候选集C(k) * 2. 根据精简后的候选集C(k)根据求出频繁集L(k) * * 数据结构： * 1. 使用List<String> 存储整个数据库的数据，string代表每一行的数据 * 2. 使用HashMap<String,Integer> 存储项集以及项集的重复次数，便于查找。是{项集}的集合。 * 3. 每个项集用String类型存储。项集中的每一项使用String.split(" ")方法变成数据，以便取用项集中的每一项 * * 给定的数据格式： * 1. 每个数据以空格隔开 * 2. 每一行代表一个事务T,事务编号就是行号 * * 示例数据： * 1. {11，12，13}是一个频繁项集，是属于HashMap<String,Integer>中的一个元素，数据类型为String. * 2. Set<String> = HashMap.keySet(); 用来存储HashMap的所有key的值，也就是存储所有的项集（不包括项集的重复次数） * * 相关概念： * 1. 支持度百分比 * 2. 支持度 * 3. 事务 * 4. 候选项集 * 5. 频繁项集 */ public class Main { private static double SUPPORT_PERCENT = 0.01; private static List<String> data2DList = new ArrayList<>(); public static void main(String[] args) { System.out.println("===================Apriori算法主程序界面===================="); //0. 输入数据 /*Scanner in=new Scanner(System.in); SUPPORT_PERCENT = in.nextDouble();*/ //1. 导入数据 importData(); //2. 进行算法处理 apriori(); } /** * Apriori算法主程序，需要递归处理 * * @return */ public static void apriori(){ //扫描整个数据库D，对每一项进行计数，获得一项的{候选项集合} Map<String, Integer> stepFrequentSetMap = new HashMap<>(); System.out.println("\n=====================第" + 1 + "次扫描的频繁项集列表======================" + "\n"); stepFrequentSetMap.putAll(getFrequentSets(findCandidateOneSets())); Set<String> stringSet = stepFrequentSetMap.keySet(); for (String string: stringSet){ System.out.println("频繁集：" + string + "支持度:" + stepFrequentSetMap.get(string)); } System.out.println("\n频繁项集的个数：" + stringSet.size()); int i = 1; //当生成的频繁项集为空的时候，退出循环 while(stepFrequentSetMap != null && stepFrequentSetMap.size()>0){ i++; //打印当前的频繁项集的信息 System.out.println("\n=====================第" + i + "次扫描的频繁项集列表======================" + "\n"); stepFrequentSetMap = getFrequentSets(getMinCandidate(stepFrequentSetMap)); if (stepFrequentSetMap != null){ stringSet = stepFrequentSetMap.keySet(); for (String string: stringSet){ System.out.println("频繁集：" + string + "支持度:" + stepFrequentSetMap.get(string)); } System.out.println("\n频繁项集的个数：" + stringSet.size()); } } } /** * 导入数据 * * @return 集合和集合，即二维集合 */ private static void importData(){ File file = new File("retail.dat"); try { //文件存在且为文件类型执行接下来的操作 if (file.isFile() && file.exists()){ InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "UTF-8"); BufferedReader bufferedReader = new BufferedReader(reader); String lineTxt; while ((lineTxt = bufferedReader.readLine()) != null){//读取文件中的一行 data2DList.add(lineTxt); } reader.close(); }else { System.err.println("找不到指定文件！"); } }catch (Exception e){ System.err.println("读取文件内容出错！"); e.printStackTrace(); } } /** * 寻找1项的候选集C1（起始化特殊的处理） * * @return 返回map集合，这个集合是每一项以及对应的重复次数：key->value * * key：每一个候选项/频繁项 * value：相应候选项/频繁项的重复次数 */ private static HashMap<String, Integer> findCandidateOneSets() { HashMap<String, Integer> resultSetMap = new HashMap<>(); for(String dataList :data2DList) { String[] dataString = dataList.split(" "); //查询map集合中是否有该元素，如果没有，加入该元素，否则给该元素对于的value(重复次数）+1 for (String string :dataString){ string += " "; if (resultSetMap.get(string) == null){ resultSetMap.put(string,1); }else { resultSetMap.put(string,resultSetMap.get(string) + 1); } } } return resultSetMap; } /** * 从L(k-1)频繁集合中得到精简的C(k)候选集 * * 1. {连接 + 剪枝} = {精简后的候选集C(k)} * 是对L(k-1)频繁集的每一个集合，进行[边连接成候选集，边剪枝]而不是先全部生成了{候选集}，然后再进行一次大循环，进行剪枝，这样会增加时间复杂度 * * 先验原则：若某个集合存在一个非空子集不是频繁项集，则该集合不是频繁项集 * * 1. 根据先验原则压缩候选集大小后得到精简的候选集集合 * 2. 对精简过的候选项集进行累加计数 * * @param frequentMapSet * @return 返回C(k)候选集合 */ private static Map<String, Integer> getMinCandidate(Map<String, Integer> frequentMapSet){ //需要返回的精简过后的候选项集 Map<String,Integer> minCandidateMapSet = new HashMap<>(); //导入频繁项集的KEY Set<String> frequentSet = frequentMapSet.keySet(); /** * 1. {自连接过程:产生候选项集} * * 对于每一个项集，拆开成项的数组。将该项集的每一项与其他项集的每一项比较， * 如果有不重复的项，就将该项与原项集链接起来，组成K项的候选项项集。 */ for (String frequentItemList1: frequentSet){ for (String frequentItemList2: frequentSet){ String[] itemArray1 = frequentItemList1.split(" "); String[] itemArray2 = frequentItemList2.split(" "); //自连接生成的候选项集 String linkString = ""; boolean flag = true;//是否可以连接 /** * 判断是否可以自连接的条件： * 1. 前K-1项必须相同 * 2. itemArray1的最后一项必须小于第二个项集的最后一项 * 自连接过程： * 将第一个项集与第二个项集的最后一项连接起来 */ for (int i =0;i<itemArray1.length -1 ;i++){ if (!itemArray1[i].equals(itemArray2[i])){ flag = false; break; } } if (flag && itemArray1[itemArray1.length - 1].compareTo(itemArray2[itemArray1.length -1]) < 0){ linkString = frequentItemList1 + itemArray2[itemArray2.length - 1] + " "; } /** * 2. {剪枝过程，也就是先验规则的使用} * * 找出该候选集的所有子集，并判断每个子集是否属于频繁子集 */ boolean hasInfrequentSubSet = false;// 是否有非频繁子项集，默认无 if (linkString != ""){//自连接成功 //System.out.println(linkString); //候选项集的所有项的数组 String[] itemArray = linkString.split(" "); //*重点：找出该候选集的所有子集，实际操作的时候只需要找出比候选集少一项的子集集合 //*重点：因为这都是不断的递归上来的，项数更小的项集肯定是频繁项集 for (int i = 0; i <itemArray.length; i++){ String subString = ""; for (int j = 0;j<itemArray.length; j++){ if (j!=i){ subString += itemArray[j] + " "; } } if (frequentMapSet.get(subString) == null){ hasInfrequentSubSet = true; break; } } }else{ hasInfrequentSubSet = true;//这里并不是代表存在非频繁子集，只是表示没有自连接成功，没有找到候选集 } //自连接成功，加入到候选集集合中 if (!hasInfrequentSubSet){ minCandidateMapSet.put(linkString,0); } } } /** * 3. {对生成的候选集进行统计支持度} */ Set<String> minCandidateSet = minCandidateMapSet.keySet(); // 将每一行的候选项集，由String数据变成字符串数组。 // 将每一行的事务，由String转换成List<String> for (String itemList: minCandidateSet){ String[] strings = itemList.split(" "); int num = 0; for (String data:data2DList){ List<String>dataList = Arrays.asList(data.split(" ")); Boolean flag = true; //如果候选项集中有一项在当前事务中找不到，支持度则不会增加 for (int i =0;i < strings.length;i++){ if (!dataList.contains(strings[i])){ flag = false; break; } } if (flag){ minCandidateMapSet.put(itemList,minCandidateMapSet.get(itemList) + 1); } } } return minCandidateMapSet; } /** * 从精简后的C(k)候选集中得到L(k)频繁集合 * * {统计精简后的候选集C(k)的重复次数} = {最后得到L(k)频繁集} * * 1. 对精简过的候选集进行判断（之前已经做好了计数工作），不满足支持度的进行排除 * * @param minCandidateMapSet * @return */ private static Map<String, Integer> getFrequentSets(Map<String, Integer> minCandidateMapSet){ if (minCandidateMapSet == null){ //精简后的候选集为空，表示当前项数的候选集不存在，此时需要结束该算法了 System.err.println("候选项集为空"); return null; }else{ Map<String,Integer> frequentMapSet = new HashMap<>();//需要返回的频繁项集 Set<String> minCandidateSet = minCandidateMapSet.keySet();//获取候选项集的KEY，也就是所有的项集的具体数据 Double SUPPORT = (data2DList.size() * SUPPORT_PERCENT);//最小支持度 //Double SUPPORT = 5.0; System.out.println("最小支持度为：" + SUPPORT + " 候选项集的大小为：" + minCandidateMapSet.size() + "\n"); for (String itemListString: minCandidateSet){ //如果该项集的重复次数大于或者等于最小支持度，就把该项加入到频繁项即中 if (minCandidateMapSet.get(itemListString) >= SUPPORT){ frequentMapSet.put(itemListString,minCandidateMapSet.get(itemListString)); } } if (frequentMapSet.size() == 0){ //计算得到的频繁项集为空，表示此时循环应该结束了 return null; }else{ return frequentMapSet; } } } }
开学一周啦！看了好多宿舍的摆设，也买了很多小部件打扮了自己的新宿舍，下面总结一点经验，如果能帮助到你，那就太好了。首先整理收纳个人认为一定要基于以下几个原则：东西要多是收纳的前提。如果桌子上就一个电脑，也没有书籍就基本上不用什么整理收纳收纳的目的是为了能够方便找到，当然好看也是很重要的。只要方便找到常用的问题，才能方便归还，才能持续保持整齐。收纳的每一部分不要装太多东西，更重要的是分类别装。不是说有多大空间就要塞满多少，塞得太满。就比如手机多个App整理成一个文件夹，总不会把很多应用装到一个文件夹里，是一个道理。整理桌面电线电线是整理桌面最为关键的一部分。如果你的电线横七竖八，乱七八糟就基本不可能有很舒服的桌面了。首先你可能需要你个插口多一点的插线板（插排）。你得计算你常用的充电口有哪些，对我来说：手机充电器电脑充电器小米手环USB充电口但这不是意味着你买的插排只要两个口，因为有的电脑充电器很占位置（比如MacBook的充电器就很不科学）。对于常用的充电器就别拔下来，因为拔来拔去不仅很麻烦，而且拔下来放哪里呢？最重要的是插排和电线一定要尽量的藏起来藏起来：简单的方法就是和我一样，我用个收纳盒挡起来了，在第一张图中的最右边就是我的插排。你也可以买一个电线收纳盒，淘宝上有很多，就不多介绍了。电线总是很长，所以一定是需要这个来拯救你：电线捆绑带只要9元，没有看错，买了20条我只用了几条，还多了很多。绑在电线上然后再藏起来就很帅！整理桌面物品桌子上解决了电线问题，还有各种常用物品的整理：书本、钥匙、镜子、梳子、杯子、常用药物等等小东西。所以我买了一个医药箱：这个很便宜，外表是塑料的，大小也适中，我觉得可以。其实自己药也不多，感冒药、维生素B2、B6、西瓜喷剂。但是不放在一起就很容易乱和丢失。我又买了一个桌面收纳盒：我买的这个我以为是木质的（虚假广告！）收到货之后发现是那种高压纸浆的材质，没有木质的重，但是组装起来就比较简单，最终的效果还是不错的。你可以选择买抽屉式牛皮纸盒 + 书立。提升生活品质的小物件台灯对于上床下铺的宿舍没有台灯，可以说晚上是很黑的。买一款很帅灯光很舒服的台灯非常有必要：我买的是下面这款：特点是可以拉伸调整角度，缺点是无法调整亮度，只有一种光度。真实发光没有图片那么白，其实有点泛黄的灯光。还有一种台灯就是宿舍灯管，可以吸附在书桌顶部，但是我觉得虽然看起来很炫酷，但是可能有点鸡肋，还不能调整角度。电脑支架使用笔记本电脑时间长点的话，因为屏幕太矮的问题，腰部就会很酸疼。有一中是电脑支架桌子，但是那种支架就要搭配一个机械键盘，暂时目前还没有考虑入手机械键盘，就买了这款斜坡式的电脑支架。电脑支架选择标准：外观一定要很好看！拒绝那种全黑的，有很大的那种支架。可以调节高度我买的这个考虑了很久，确实很贵啊！最后还是剁手了。质量还是不错的，现在用起来一切良好。床单固定器这个不多说，宿舍经验的都知道床单很可能一晚上就已经翻天覆地了，每天都起床整理也有点费劲和麻烦，我买的下面这款，用起来效果还可以，就是夹被子的时候有点吃力：墙壁挂钩墙壁挂钩真的超级方便的，可以挂一些外套呀（虽然有衣柜，但是常穿的挂在外面很方便哒）可以挂一些零食（从超市买回的零食用袋子装起来挂在墙上， 想吃就拿点，美滋滋）可以挂洗脸的毛巾（把毛巾挂在衣柜里不利于晒干，但是每次挂在晾衣架上也很麻烦）我买的是这款：以上。